├── .gitignore
├── images
├── image1.png
├── image2.png
├── image3.png
├── image4.png
├── image5.png
├── image6.png
├── image7.png
├── image8.png
├── image9.png
└── ConnectConfigSIM.png
├── demo_agreements
├── .DS_Store
├── EmployeeNDA_BO-8.docx
├── MSA_BO-8_Keranos.docx
├── NDA_BO-8_Fontara_v1.docx
├── SOW_BO-8_Fontara_2.docx
├── SOW_BO-8_Fontara_Mktg.docx
├── MSA_BO-8_Innovate Global.docx
├── MSA_BO-8_Momentum Driver.docx
├── MSA_BO-8_Nesis Biotech.docx
├── MSA_BO-8_Pathway Placers.docx
├── MSA_BO-8_Stellar Logical.docx
├── MSA_BO-8_Fontara_Mktg_OLD.docx
├── MSA_BO-8_Insight Baybridge.docx
├── SOW_BO-8_Insight Baybridge.docx
├── MSA_BO-8_CloudMatrix Dynamics.docx
├── MSA_BO-8_DataVault Dynamics.docx
├── MSA_BO-8_EvoLink Networks LLC.docx
├── MSA_BO-8_MarketPulse Dynamics.docx
├── MSA_BO-8_SmartFactory Systems.docx
├── MSA_BO-8_TechVerse Solutions.docx
├── SOW_BO-8_MarketPulse Dynamics.docx
├── SOW_BO-8_SmartFactory Systems.docx
├── MSA_BO-8_Helix Learning _.docx
├── MSA_BO-8_ScoutVision Recruiting.docx
├── MSA_BO-8_SmartPulse Enterprises.docx
├── MSA_BO-8_Virtual Smart Products.docx
├── MSA_BO-8_HyperGrid Design Agency.docx
├── MSA_BO-8_Virtual Tech Collective.docx
├── SOW_BO-8_Virtual Tech Collective.docx
├── CERTIFICATE OF INSURANCE_BO-8_Helix.docx
├── License_BO-8_Virtual Tech Collective.docx
├── CERTIFICATE OF INSURANCE_BO-8_Pathway.docx
├── MSA_BO-8_Dynamic Skillz Strategies Inc.docx
└── MSA_BO-8_Hidden Continent Environmental.docx
├── src
├── deleteAgreement.js
├── client.js
├── getAgreements.js
├── auth.js
└── bulkUploadAgreements.js
├── example.env
├── package.json
├── bulkUploadWithAPI.md
├── createWebhooks.md
├── oauthWithAIAssistant.md
├── server.js
├── public
└── index.html
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | node_modules/
3 |
4 | # Environment variables
5 | .env
--------------------------------------------------------------------------------
/images/image1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/images/image1.png
--------------------------------------------------------------------------------
/images/image2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/images/image2.png
--------------------------------------------------------------------------------
/images/image3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/images/image3.png
--------------------------------------------------------------------------------
/images/image4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/images/image4.png
--------------------------------------------------------------------------------
/images/image5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/images/image5.png
--------------------------------------------------------------------------------
/images/image6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/images/image6.png
--------------------------------------------------------------------------------
/images/image7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/images/image7.png
--------------------------------------------------------------------------------
/images/image8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/images/image8.png
--------------------------------------------------------------------------------
/images/image9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/images/image9.png
--------------------------------------------------------------------------------
/demo_agreements/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/.DS_Store
--------------------------------------------------------------------------------
/images/ConnectConfigSIM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/images/ConnectConfigSIM.png
--------------------------------------------------------------------------------
/demo_agreements/EmployeeNDA_BO-8.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/EmployeeNDA_BO-8.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Keranos.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Keranos.docx
--------------------------------------------------------------------------------
/demo_agreements/NDA_BO-8_Fontara_v1.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/NDA_BO-8_Fontara_v1.docx
--------------------------------------------------------------------------------
/demo_agreements/SOW_BO-8_Fontara_2.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/SOW_BO-8_Fontara_2.docx
--------------------------------------------------------------------------------
/demo_agreements/SOW_BO-8_Fontara_Mktg.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/SOW_BO-8_Fontara_Mktg.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Innovate Global.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Innovate Global.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Momentum Driver.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Momentum Driver.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Nesis Biotech.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Nesis Biotech.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Pathway Placers.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Pathway Placers.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Stellar Logical.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Stellar Logical.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Fontara_Mktg_OLD.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Fontara_Mktg_OLD.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Insight Baybridge.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Insight Baybridge.docx
--------------------------------------------------------------------------------
/demo_agreements/SOW_BO-8_Insight Baybridge.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/SOW_BO-8_Insight Baybridge.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_CloudMatrix Dynamics.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_CloudMatrix Dynamics.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_DataVault Dynamics.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_DataVault Dynamics.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_EvoLink Networks LLC.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_EvoLink Networks LLC.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_MarketPulse Dynamics.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_MarketPulse Dynamics.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_SmartFactory Systems.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_SmartFactory Systems.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_TechVerse Solutions.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_TechVerse Solutions.docx
--------------------------------------------------------------------------------
/demo_agreements/SOW_BO-8_MarketPulse Dynamics.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/SOW_BO-8_MarketPulse Dynamics.docx
--------------------------------------------------------------------------------
/demo_agreements/SOW_BO-8_SmartFactory Systems.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/SOW_BO-8_SmartFactory Systems.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Helix Learning _.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Helix Learning _.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_ScoutVision Recruiting.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_ScoutVision Recruiting.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_SmartPulse Enterprises.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_SmartPulse Enterprises.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Virtual Smart Products.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Virtual Smart Products.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_HyperGrid Design Agency.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_HyperGrid Design Agency.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Virtual Tech Collective.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Virtual Tech Collective.docx
--------------------------------------------------------------------------------
/demo_agreements/SOW_BO-8_Virtual Tech Collective.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/SOW_BO-8_Virtual Tech Collective.docx
--------------------------------------------------------------------------------
/demo_agreements/CERTIFICATE OF INSURANCE_BO-8_Helix.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/CERTIFICATE OF INSURANCE_BO-8_Helix.docx
--------------------------------------------------------------------------------
/demo_agreements/License_BO-8_Virtual Tech Collective.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/License_BO-8_Virtual Tech Collective.docx
--------------------------------------------------------------------------------
/demo_agreements/CERTIFICATE OF INSURANCE_BO-8_Pathway.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/CERTIFICATE OF INSURANCE_BO-8_Pathway.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Dynamic Skillz Strategies Inc.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Dynamic Skillz Strategies Inc.docx
--------------------------------------------------------------------------------
/demo_agreements/MSA_BO-8_Hidden Continent Environmental.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docusign/docusign-discover-workshop-2/main/demo_agreements/MSA_BO-8_Hidden Continent Environmental.docx
--------------------------------------------------------------------------------
/src/deleteAgreement.js:
--------------------------------------------------------------------------------
1 | // Deletes a single agreement by its ID.
2 | import { makeClient } from './client.js';
3 |
4 | export async function deleteAgreement({ agreementId, force = false, accessToken } = {}) {
5 | // TODO: Add sdk call to delete an agreement.
6 | return { ok: true, deletedId: agreementId };
7 | }
8 |
--------------------------------------------------------------------------------
/example.env:
--------------------------------------------------------------------------------
1 | DS_CLIENT_ID={DS_CLIENT_ID}
2 | DS_SECRET_KEY={DS_SECRET_KEY}
3 | DS_ACCESS_TOKEN={DS_ACCESS_TOKEN}
4 | DS_REDIRECT_URI=http://localhost:3000/ds/callback
5 | DS_ACCOUNT_ID=12345678-1234-1234-1234-123456788912 #{DS_ACCOUNT_ID}
6 | BASE_URL=https://ff8f3e28-f5fd-473e-afc3-1ae502d0f0c5.mock.pstmn.io #https://api-d.docusign.com
7 | DS_ENV=development
--------------------------------------------------------------------------------
/src/client.js:
--------------------------------------------------------------------------------
1 | import { IamClient } from '@docusign/iam-sdk';
2 |
3 | export function makeClient(accessToken) {
4 | if (!accessToken) throw new Error('Access token required')
5 |
6 | return new IamClient({
7 | basePath: process.env.DS_ENV === 'prod'
8 | ? 'https://api.docusign.com'
9 | : 'https://api-d.docusign.com',
10 | accessToken
11 | })
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "navigator-mini-dashboard",
3 | "version": "1.0.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "start": "node server.js",
8 | "dev": "nodemon server.js"
9 | },
10 | "dependencies": {
11 | "@docusign/iam-sdk": "^1.0.0-beta.3",
12 | "cookie-parser": "^1.4.7",
13 | "dotenv": "^16.4.5",
14 | "express": "^4.19.2",
15 | "express-session": "^1.18.2",
16 | "morgan": "^1.10.0"
17 | },
18 | "devDependencies": {
19 | "nodemon": "^3.1.10"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/bulkUploadWithAPI.md:
--------------------------------------------------------------------------------
1 | # Bulk Upload using the Navigator API
2 |
3 | ## Bulk Upload Steps
4 |
5 | Bulk upload consists of 3 steps:
6 |
7 | **1. Create the job**
8 |
9 | POST to `https://{{host}}/v1/accounts/{{accountId}}/upload/jobs`, passing a JSON body:
10 |
11 | ```json
12 | {
13 | "job_name":"test_name",
14 | "expected_number_of_docs":1,
15 | "language":"en_us"
16 | }
17 | ```
18 |
19 | A successful response includes one or more unique upload URLs corresponding to the `expected_number_of_docs` value in the request.
20 |
21 | **2. Upload documents**
22 |
23 | PUT to each unique URL, passing a document in the body as binary data.
24 |
25 | A successful response is a 201 with no data.
26 |
27 | **3. Complete the upload**
28 |
29 | POST to `https://{{host}}/v1/accounts/{{accountId}}/upload/jobs/{{jobId_int}}/actions/complete`.
30 |
31 | A successful response provides data about the job, including the status.
32 |
33 | ## Check Bulk Upload Status
34 |
35 | GET `https://{{host}}/v1/accounts/{{accountId}}/upload/jobs/{{jobId_int}}`.
36 |
37 | A successful response provides data about the job, including the status.
--------------------------------------------------------------------------------
/createWebhooks.md:
--------------------------------------------------------------------------------
1 | # Create Webhooks for Navigator Events
2 |
3 | ## Navigator Webhook Events
4 |
5 | | Event | Trigger |
6 | |----------------------------------|-----------------------------------------------------|
7 | | `agreement-created` | a new agreement is created in Navigator |
8 | | `agreement-extractions-reviewed` | the number of pending extraction reviews is reduced |
9 | | `agreement-reviews-complete` | the number of pending extraction reviews reaches 0 |
10 | | `agreement-updated` | a user manually modifies an agreement in Navigator |
11 | | `agreement-deleted` | an agreement is deleted in Navigator |
12 |
13 | ## Creating Custom Configurations Using Navigator Events
14 |
15 | These events are available only for custom Connect configurations created using the **JSON SIM** event model message format.
16 |
17 | To create a JSON SIM configuration, use the default values for **data format** (REST v2.1) and **event message delivery mode** (SIM) as shown in the following image:
18 |
19 | 
20 |
21 |
--------------------------------------------------------------------------------
/src/getAgreements.js:
--------------------------------------------------------------------------------
1 | // Lists agreements using the Docusign IAM Navigator SDK.
2 | import { makeClient } from './client.js';
3 |
4 | // Mock data
5 | // Lists agreements using the Docusign IAM Navigator API directly
6 | export async function getAgreements({ accessToken } = {}) {
7 | // if (!accessToken) throw new Error('Access token required');
8 | const accountId = process.env.DS_ACCOUNT_ID;
9 | // if (!accountId) throw new Error('ACCOUNT_ID missing');
10 |
11 | const baseUrl = process.env.BASE_URL;
12 |
13 | try {
14 | const res = await fetch(`${baseUrl}/v1/accounts/${accountId}/agreements`, {
15 | method: 'GET',
16 | headers: {
17 | 'Authorization': `Bearer ${accessToken}`,
18 | 'Accept': 'application/json',
19 | 'Content-Type': 'application/json'
20 | }
21 | });
22 |
23 | if (!res.ok) {
24 | const text = await res.text();
25 | let error;
26 | try {
27 | const json = JSON.parse(text);
28 | error = json.message || json.error || text;
29 | } catch (e) {
30 | error = text;
31 | }
32 | throw new Error(`Agreements API failed: ${res.status} ${error}`);
33 | }
34 |
35 | const data = await res.json();
36 |
37 | const items = (data?.items ?? data?.data ?? []);
38 | console.log(`Agreements fetched: ${JSON.stringify(items, null, 2)}`);
39 |
40 | // Normalize response shape
41 | return {
42 | items: items.map(a => ({
43 | agreementId: a.agreementId ?? a.id ?? '',
44 | name: a.name ?? a.title ?? '',
45 | status: a.review_status ?? 'unknown',
46 | category: a.category ?? 'unknown',
47 | raw: a
48 | }))
49 | };
50 | } catch (err) {
51 | console.error('getAgreements failed:', err);
52 | throw err;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/auth.js:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import { AuthUtils, IamClient } from '@docusign/iam-sdk';
3 |
4 | const CLIENT_ID = process.env.DS_CLIENT_ID;
5 | const SECRET_KEY = process.env.DS_SECRET_KEY;
6 | const REDIRECT_URI = process.env.DS_REDIRECT_URI;
7 |
8 | if (!CLIENT_ID || !SECRET_KEY) {
9 | console.warn('Missing DS client id / secret. Set DS_CLIENT_ID and DS_SECRET_KEY.');
10 | }
11 |
12 | function buildAuthUrl({
13 | redirectUri = REDIRECT_URI,
14 | scopes = [
15 | "adm_store_unified_repo_read",
16 | "adm_store_unified_repo_write",
17 | "document_uploader_write",
18 | "document_uploader_read",
19 | "aow_manage",
20 | "signature"
21 | ],
22 | state = ''
23 | } = {}) {
24 | if(process.env.DS_ACCESS_TOKEN) {
25 | return "/ds/callback";
26 | }
27 |
28 | if (!CLIENT_ID) throw new Error('CLIENT_ID missing');
29 | return AuthUtils.createAuthorizationUrl({
30 | type: 'code',
31 | clientId: CLIENT_ID,
32 | redirectUri,
33 | scopes,
34 | state,
35 | });
36 | }
37 |
38 | /**
39 | * Exchange authorization code for tokens using the @docusign/iam-sdk
40 | * returns { accessToken, refreshToken, expiresIn, raw }
41 | */
42 | async function exchangeCodeForToken(code, { redirectUri = REDIRECT_URI } = {}) {
43 | if (!code) throw new Error('authorization code is required');
44 | if (!CLIENT_ID || !SECRET_KEY) throw new Error('CLIENT_ID or SECRET_KEY missing');
45 |
46 | const iam = new IamClient();
47 | try {
48 | // TODO: Implement token exchange via sdk
49 |
50 | return {};
51 | } catch (err) {
52 | const msg = err?.errorDescription ?? err?.error_description ?? err?.message ?? String(err);
53 | const e = new Error(`Token exchange failed: ${msg}`);
54 | e.cause = err;
55 | throw e;
56 | }
57 | }
58 |
59 | /**
60 | * Optional: get userinfo via the IAM client
61 | */
62 | async function getUserInfo(accessToken) {
63 | if (!accessToken) throw new Error('accessToken is required');
64 | const iam = new IamClient();
65 | try {
66 | const info = await iam.auth.getUserInfo({ accessToken });
67 | return info;
68 | } catch (err) {
69 | const msg = err?.message ?? String(err);
70 | const e = new Error(`getUserInfo failed: ${msg}`);
71 | e.cause = err;
72 | throw e;
73 | }
74 | }
75 |
76 | export {
77 | buildAuthUrl,
78 | exchangeCodeForToken,
79 | getUserInfo,
80 | };
--------------------------------------------------------------------------------
/oauthWithAIAssistant.md:
--------------------------------------------------------------------------------
1 | # Log into your Docusign account with Navigator access
2 |
3 | Go to [apps-d.docusign.com](http://apps-d.docusign.com) and sign in with your developer account with Navigator access.
4 | Go to the Agreements tab and click completed to confirm that you have access. You should see a tag that says AI assisted.
5 |
6 | # Obtain an access token Using the Docusign Developer AI Assistant for VS Code
7 |
8 | 1. [Set up GitHub Copilot in VS Code](https://code.visualstudio.com/docs/copilot/setup)
9 | 2. Install the Docusign Developer AI Assistance for VS Code extension in VS Code extensions marketplace:
10 |
11 | 3. Open GitHub Copilot by clicking on the Copilot icon in the Activity Bar or using the shortcut (Ctrl+Alt+P or Cmd+Alt+P on macOS).
12 | 4. Add the AI assistant as a participant in the chat using the command @docusign and sign in with your Docusign developer account (with Navigator access).
13 |
14 | 5. Copy the example.env file from the root of the project directory and save it to a new file named .env. This is where you will save your authentication information from the following steps.
15 | 6. Enter @docusign /getAccessToken and answer the follow the prompts to create and configure an integration key and generate an access token.
16 |
17 |
18 |
19 | 7. Name your app and set the redirect URI to "https://localhost:3000/ds/callback".
20 |
21 |
22 |
23 | 8. Open the consent url and add the following scopes to the url:
24 |
25 | ```
26 | %20adm_store_unified_repo_write%20document_uploader_write%20document_uploader_read
27 | ```
28 |
29 | 9. Reload the updated url and grant consent to the listed scopes.
30 |
31 |
32 |
33 | 10. After granting consent, copy the redirect URI.
34 |
35 |
36 |
37 | 11. Paste the URI into VSCode.
38 |
39 |
40 |
41 |
42 |
43 | It should also display your user info, and an example curl request using the access token.
44 | 12. Copy the generated integration key, integration secret, and access token to your .env file as values to the respective environment variables DS\_CLIENT\_ID, DS\_SECRET\_KEY, and DS\_ACCESS\_TOKEN. Copy the account id from the displayed user info to the env variable DS\_ACCOUNT\_ID.
45 |
46 |
47 |
48 | # See your new IK on the apps and keys page:
49 |
50 | Visit [https://admindemo.docusign.com/authenticate?goTo=appsAndKeys](https://admindemo.docusign.com/authenticate?goTo=appsAndKeys)
51 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import express from 'express';
3 | import morgan from 'morgan';
4 | import session from 'express-session';
5 | import cookieParser from 'cookie-parser';
6 | import { getAgreements } from './src/getAgreements.js';
7 | import { deleteAgreement } from './src/deleteAgreement.js';
8 | import { bulkUploadAgreements, bulkUploadStatus } from './src/bulkUploadAgreements.js';
9 | import { buildAuthUrl, exchangeCodeForToken } from './src/auth.js';
10 |
11 | const app = express();
12 |
13 | // Add session support before other middleware
14 | app.use(session({
15 | secret: process.env.SESSION_SECRET || 'dev-secret-key',
16 | resave: false,
17 | saveUninitialized: false,
18 | cookie: {
19 | secure: process.env.NODE_ENV === 'production',
20 | httpOnly: true
21 | }
22 | }));
23 |
24 | app.use(morgan('dev'));
25 | app.use(express.json());
26 | app.use(express.static('public'));
27 | app.use(cookieParser());
28 |
29 | // Auth middleware to protect routes
30 | function requireAuth(req, res, next) {
31 | if (!req.session.accessToken) {
32 | if (req.headers.accept?.includes('application/json')) {
33 | return res.status(401).json({ error: 'Login required' });
34 | }
35 | return res.redirect('/auth/login');
36 | }
37 | next();
38 | }
39 |
40 | // Protected API routes
41 | app.get('/api/getAgreements', requireAuth, async (req, res) => {
42 | try {
43 | const data = await getAgreements({
44 | accessToken: req.session.accessToken
45 | });
46 | res.json(data);
47 | } catch (e) {
48 | console.error(e);
49 | res.status(500).json({ error: e.message || 'Failed to fetch agreements' });
50 | }
51 | });
52 |
53 | app.delete('/api/deleteAgreement/:agreementId', requireAuth, async (req, res) => {
54 | try {
55 | const { agreementId } = req.params;
56 | const force = req.query.force === 'true';
57 | const result = await deleteAgreement({
58 | agreementId,
59 | force,
60 | accessToken: req.session.accessToken
61 | });
62 | res.json(result);
63 | } catch (e) {
64 | const status = e.code === 'SAFE_GUARD' ? 400 : 500;
65 | res.status(status).json({ error: e.message || 'Failed to delete agreement' });
66 | }
67 | });
68 |
69 | // Bulk upload agreements
70 | app.post('/api/bulkUploadAgreements', requireAuth, express.json(), async (req, res) => {
71 | try {
72 | const result = await bulkUploadAgreements({
73 | accessToken: req.session.accessToken
74 | });
75 |
76 | // Save jobId in session for future status checks
77 | if (result) {
78 | req.session.jobId = result.jobId;
79 | }
80 |
81 | res.json(result);
82 | } catch (e) {
83 | console.error('bulk upload failed', e);
84 | res.status(500).json({ error: 'Bulk upload failed' });
85 | }
86 | });
87 |
88 | // Bulk upload status endpoint
89 | app.get('/api/bulkUploadStatus', requireAuth, async (req, res) => {
90 | try {
91 | const jobId = req.session.jobId;
92 | if (!jobId) {
93 | return res.status(400).json({ error: 'No bulk upload job found in session' });
94 | }
95 |
96 | const status = await bulkUploadStatus({ jobId, accessToken: req.session.accessToken });
97 |
98 | res.json(status);
99 | } catch (e) {
100 | console.error('bulk upload status check failed', e);
101 | res.status(500).json({ error: 'Status check failed' });
102 | }
103 | });
104 |
105 |
106 | // Auth routes
107 | app.get('/auth/login', (req, res) => {
108 | try {
109 | const state = req.query.state ?? '';
110 | const url = buildAuthUrl({ state });
111 | res.redirect(url);
112 | } catch (err) {
113 | console.error('auth/login failed', err);
114 | res.status(500).send('Auth configuration error');
115 | }
116 | });
117 |
118 | app.get('/ds/callback', async (req, res) => {
119 | if (process.env.DS_ACCESS_TOKEN){
120 | console.log("using env token");
121 | req.session.accessToken = process.env.DS_ACCESS_TOKEN;
122 | return res.redirect('/');
123 | }
124 | const { code, error } = req.query;
125 | if (error) return res.status(400).send(String(error));
126 | if (!code) return res.status(400).send('Missing code');
127 |
128 | try {
129 | const token = await exchangeCodeForToken(String(code));
130 | // Store token in session
131 | req.session.accessToken = token.accessToken;
132 | res.redirect('/');
133 | } catch (e) {
134 | console.error('auth callback failed', e);
135 | res.status(500).send('Token exchange failed');
136 | }
137 | });
138 |
139 | // Add logout route
140 | app.post('/auth/logout', (req, res) => {
141 | req.session.destroy();
142 | res.redirect('/');
143 | });
144 |
145 | const port = process.env.PORT || 3000;
146 | app.listen(port, () => {
147 | console.log(`Navigator mini dashboard at http://localhost:${port}`);
148 | });
149 |
--------------------------------------------------------------------------------
/src/bulkUploadAgreements.js:
--------------------------------------------------------------------------------
1 | // Uploads 2 agreements (in a hard coded folder).
2 | export async function bulkUploadAgreements({ accessToken } = {}) {
3 |
4 | console.log(`Bulk uploading agreements`);
5 | const accountId = process.env.DS_ACCOUNT_ID;
6 | const baseUrl = process.env.BASE_URL;
7 | try {
8 | // Upload files to each blob URL
9 | const fs = await import('fs');
10 | const path = await import('path');
11 |
12 | // Automatically read files from demo_agreements folder
13 | const demoAgreementsDir = './demo_agreements';
14 |
15 | // Function to recursively find all files
16 | const findFiles = (dir) => {
17 | const files = [];
18 | const items = fs.readdirSync(dir);
19 |
20 | for (const item of items) {
21 | const fullPath = path.join(dir, item);
22 | const stat = fs.statSync(fullPath);
23 |
24 | if (stat.isDirectory()) {
25 | // Recursively search subdirectories
26 | files.push(...findFiles(fullPath));
27 | } else if (path.extname(item).toLowerCase() === '.docx') {
28 | files.push(fullPath);
29 | }
30 | }
31 |
32 | return files;
33 | };
34 |
35 | const files = findFiles(demoAgreementsDir);
36 |
37 | if (files.length === 0) {
38 | console.warn('No files found in demo_agreements folder');
39 | return { jobId: jobId, received: 0 };
40 | }
41 |
42 | console.log(`Found ${files.length} files:`, files);
43 |
44 | // Step 1: Add a value for body:
45 | const body = {};
46 |
47 | // Step 2: Add the create job endpoint URL as the first fetch argument:
48 | const res = await fetch(``, {
49 | method: 'POST',
50 | headers: {
51 | 'Authorization': `Bearer ${accessToken}`,
52 | 'Accept': 'application/json',
53 | 'Content-Type': 'application/json'
54 | },
55 | body: JSON.stringify(body)
56 | });
57 |
58 | if (res.status >= 200 && res.status < 300) {
59 | const data = await res.json();
60 | let jobId;
61 | let i;
62 | if (data && data.id) {
63 | jobId = data.id;
64 | }
65 |
66 | if (
67 | data._embedded &&
68 | Array.isArray(data._embedded.documents) &&
69 | data._embedded.documents.length > 0
70 | ) {
71 | const blobUrls = data._embedded.documents
72 | .filter(doc => doc._actions && doc._actions.upload_document)
73 | .map(doc => doc._actions.upload_document);
74 |
75 | for (i = 0; i < Math.min(blobUrls.length, files.length); i++) {
76 | const blobUrl = blobUrls[i];
77 | const filePath = files[i];
78 |
79 | try {
80 | const fileBuffer = fs.readFileSync(filePath);
81 |
82 | const uploadRes = await fetch(blobUrl, {
83 | method: 'PUT',
84 | headers: {
85 | 'x-ms-blob-type': 'BlockBlob',
86 | 'x-ms-meta-filename': path.basename(filePath),
87 | 'x-ms-meta-myownprop': 'mytestprop',
88 | 'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
89 | },
90 | body: fileBuffer
91 | });
92 |
93 | if (uploadRes.ok) {
94 | console.log(`Successfully uploaded ${path.basename(filePath)} to blob URL ${i + 1}`);
95 | } else {
96 | console.error(`Failed to upload ${path.basename(filePath)}:`, uploadRes.status, uploadRes.statusText);
97 | }
98 | } catch (err) {
99 | console.error(`Error uploading ${path.basename(filePath)}:`, err);
100 | }
101 | }
102 | }
103 |
104 | // Step 3: Add the complete job endpoint URL as the first fetch argument:
105 | const completeRes = await fetch(``, {
106 | method: 'POST',
107 | headers: {
108 | 'Authorization': `Bearer ${accessToken}`,
109 | 'Accept': 'application/json',
110 | 'Content-Type': 'application/json'
111 | },
112 | body: JSON.stringify(body)
113 | });
114 |
115 | return {jobId: jobId, received: i};
116 |
117 | }
118 |
119 | } catch (err) {
120 | console.error('getAgreements failed:', err);
121 | throw err;
122 | }
123 |
124 | return {ok: true}
125 | }
126 |
127 | export async function bulkUploadStatus({ jobId, accessToken } = {}) {
128 | const accountId = process.env.DS_ACCOUNT_ID;
129 | const baseUrl = process.env.BASE_URL;
130 |
131 | // Step 4: Complete the statusCheck constant with a fetch statement to the check status endpoint:
132 | const statusCheck = await fetch();
133 |
134 | const statusC = await statusCheck.json();
135 |
136 | console.log(JSON.stringify(statusC, null, 2));
137 |
138 | return {status: statusC.status}
139 | }
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
| Name | 34 |ID | 35 |Review Status | 36 |Category | 37 |Actions | 38 |
|---|