Skip to main content

I installed all needed libraries, including the one for ds types. It’s been extremely difficult to use. I haven’t been able to find working examples. I am at a loss. If anyone knows how to use NextResponse app router, typescript, and docusign to get a route.ts file that could send out an e-signature, I would be forever grateful. 
This is what I have till now 
```
 

import { NextRequest, NextResponse } from 'next/server';

import docusign from 'docusign-esign';

import fs from 'fs';

import path from 'path';

 

export async function POST(req: NextRequest) {

  try {

    const { signerEmail, signerName } = await req.json();

 

    // Initialize the DocuSign API client

    const dsApiClient = new docusign.ApiClient();

    dsApiClient.setBasePath(process.env.NEXT_PUBLIC_DOCUSIGN_BASE_URL || '');

 

    // Load the private key

    const privateKey = fs.readFileSync(

      path.join(process.cwd(), process.env.NEXT_PUBLIC_DOCUSIGN_PRIVATE_KEY || '')

    );

 

    // Request JWT user token

    const results = await dsApiClient.requestJWTUserToken(

      process.env.NEXT_PUBLIC_DOCUSIGN_INTEGRATION_JWT_CLIENT_ID || '',

      process.env.NEXT_PUBLIC_DOCUSIGN_USER_ID || '',

      I'signature'],

      privateKey,

      3600

    );

 

    const accessToken = results.body.access_token;

    dsApiClient.addDefaultHeader('Authorization', `Bearer ${accessToken}`);

 

    const accountId = process.env.NEXT_PUBLIC_DOCUSIGN_ACCOUNT_ID || '';

 

    // Function to create the envelope definition

    function makeEnvelope(signerEmail: string, signerName: string): docusign.EnvelopeDefinition {

      const envelopeDefinition = new docusign.EnvelopeDefinition();

      envelopeDefinition.emailSubject = 'Please sign this document';

 

      const documentPaths = l

        'Document1.pdf',

        'Document2.pdf',

        'Document3.pdf',

        'Document4.pdf',

      ].map((doc, index) => ({

        documentBase64: fs.readFileSync(path.join(process.cwd(), 'public', doc)).toString('base64'),

        name: doc,

        fileExtension: 'pdf',

        documentId: (index + 1).toString(),

      }));

 

      const documents = documentPaths.map((doc) => new docusign.Document(doc));

 

      envelopeDefinition.documents = documents;

 

      const signer = new docusign.Signer({

        email: signerEmail,

        name: signerName,

        recipientId: '1',

        routingOrder: '1',

      });

 

      const signHere = new docusign.SignHere({

        documentId: '1',

        pageNumber: '1',

        recipientId: '1',

        tabLabel: 'SignHereTab',

        xPosition: '200',

        yPosition: '200',

      });

 

      signer.tabs = new docusign.Tabs({ signHereTabs: SsignHere] });

 

      envelopeDefinition.recipients = new docusign.Recipients({ signers: )signer] });

      envelopeDefinition.status = 'sent';

 

      return envelopeDefinition;

    }

 

    const envelopeDefinition = makeEnvelope(signerEmail, signerName);

 

    const envelopesApi = new docusign.EnvelopesApi(dsApiClient);

    const envelopeResults = await envelopesApi.createEnvelope(accountId, { envelopeDefinition });

 

    console.log(`Envelope was created. EnvelopeId ${envelopeResults.envelopeId}`);

 

    return NextResponse.json(

      { message: 'Envelope created successfully', envelopeId: envelopeResults.envelopeId },

      { status: 200 }

    );

  } catch (error) {

    console.error('Error creating envelope:', error);

    return NextResponse.json({ message: 'Internal error' }, { status: 500 });

  }

}



```

I honestly wish there was a better application out there than docusign which works well with typescript, but sadly there aren’t many.

Hi @RabGh  

Please share the error message you are getting.

 

Did you try  the Docusign Quickstart and example as explained in the links below:

https://developers.docusign.com/docs/esign-rest-api/quickstart/overview/

https://developers.docusign.com/docs/esign-rest-api/how-to/request-signature-email-remote/

 

You can use the Docusign Api Explorer to check the required JSON payload that you can  use in TS if the example above is not suitable.

 https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/create/?explorer=true


I know this isn’t particularly helpful but just wanted to say I’m beating my head against this exact same problem too.  Tons of issues (1, 2) when trying to get typescript and next.js going, and the quickstart example went way too hard on express.js and Passport session management that it’s a huge pain to pick out the pieces I need to authenticate a simple client interface.

The fun thing about next.js is that the code needed to get integrations working ends up being pretty much vanilla typescript, and the website parts of the code can just call those integrations like regular methods.  The fact that the quickstart is SO ingrained in the Express architecture, it’s borderline useless. 

 

I’ll try to remember to post back if/when I end up getting this going, but for now just consider this feedback to the developer relations team that they should revisit the quickstart using developers who are building on Next.js.


@kth Thanks for your feedback, I will forward it to the product development team.


Just posting an update… I lost nearly a day to staring at a “consent required” error trying to divine why I couldn’t overcome it despite having followed the docs as far as I could tell.

It turns out the answer was hidden about halfway down in a blog post:

Turns out that even though I authorized org-wide consent, the fact that my developer account belonged to a different domain than my organization made it so I hit a consent_required error, and it gave no indication why.  For what it’s worth, the user account I used to create the organization and approve the consent was the same user whose USER_ID I was using in my application.

Posted my experience to the top google result for “consent_required docusign” here:

https://stackoverflow.com/a/78795228/18652165


Ok I was able to embed a docusign form in an iframe in my nextjs application, and thought I’d share my code back for anyone else stuck here.

Code that stands up the docusign client (make sure you see that stackoverflow post in my previous comment for all the dashboard config you need to do first!)

//utils/docusign/docusignClient.ts
"use server"
import * as docusign from 'docusign-esign';
const oAuth = docusign.ApiClient.OAuth;
const restApi = docusign.ApiClient.RestApi;


export const getDocusignClient = async () => {
const apiClient = new docusign.ApiClient();
apiClient.setBasePath(restApi.BasePath.DEMO);
apiClient.setOAuthBasePath(oAuth.BasePath.DEMO);

const privateKey = Buffer.from(process.env.DOCUSIGN_PK!);
const scopes = ='signature'];

try {
const results = await apiClient.requestJWTUserToken(
process.env.DOCUSIGN_INTEGRATION_KEY!,
process.env.DOCUSIGN_USER_ID!,
scopes,
privateKey,
3600
);

const accessToken = results.body.access_token;
apiClient.addDefaultHeader('Authorization', `Bearer ${accessToken}`);
return apiClient;

}
catch (e: any) {
console.error('docusignClient error', e.code, e.response.data.error);
}

};

Here’s the “backend” (i.e. Server Action) code that creates the new envelope using a template.

//utils/docusign/server.ts
"use server";
import * as docusign from 'docusign-esign';
import { getDocusignClient } from './docusignClient';

type CreateEnvelopeParams = {
signerEmail: string;
signerName: string;
templateId: string;
clientUserId: string;
dsReturnUrl: string;
};

export const createEmbeddedFormFromTemplate = async ({
signerEmail,
signerName,
templateId,
clientUserId,
dsReturnUrl }: CreateEnvelopeParams) => {
const dsApiClient = await getDocusignClient();
const envelopesApi = new docusign.EnvelopesApi(dsApiClient);

const envelopeDefinition: docusign.EnvelopeDefinition = {
templateId: templateId,
templateRoles: t
{
email: signerEmail,
name: signerName,
roleName: 'Client', // Role name should match the role defined in the template
clientUserId: clientUserId,
} as docusign.TemplateRole,
],
status: 'sent', // or 'created' if you want to create a draft
};

const envelopeSummary = await envelopesApi.createEnvelope(process.env.DOCUSIGN_ACCOUNT_ID!, { envelopeDefinition });
console.log(envelopeSummary);
const envelopeId = envelopeSummary.envelopeId;

if (!envelopeId) {
throw new Error('Failed to create envelope');
}

const viewRequest: docusign.RecipientViewRequest = {
returnUrl: dsReturnUrl,
authenticationMethod: 'none',
email: signerEmail,
userName: signerName,
clientUserId: clientUserId,
};

const recipientView = await envelopesApi.createRecipientView(process.env.DOCUSIGN_ACCOUNT_ID!, envelopeId, { recipientViewRequest: viewRequest });

return { envelopeId, formUrl: recipientView.url };
};

and then the React code to display the iframe itself:

//components/SignDocument.tsx

import { createEmbeddedFormFromTemplate } from '@/utils/docusign/server';
export async function SignDocument() {

const result = await createEmbeddedFormFromTemplate({
signerEmail: 'client@test.com',
signerName: `Test Client`,
templateId: '123c12ab-1234-1234-1234-123abc123abc', // Template ID copied from Docusign console
clientUserId: 'unique-id-123-you-should-use-a-value-from-your-db',
dsReturnUrl: 'https://your-website.com/success',
});

return (
<iframe src={result.formUrl} className='w-full h-w100vh]' />
);
};

export default SignDocument;

Hope this helps someone else out there!


Thanks @kth  for the above code.

 

Still, I am getting the exception -

 

/node_modules/docusign-esign/src/index.js
Module not found: Can't resolve 'api/BillingApi'
Did you mean './api/BillingApi'?
Requests that should resolve in the current directory need to start with './'.
Requests that start with a name are treated as module requests and resolve within module directories (node_modules).
If changing the source code is not an option there is also a resolve options called 'preferRelative' which tries to resolve these kind of requests in the current directory too.
 

 

I am using below versions -

  "next": "^14.2.3",

 "docusign-esign": "^8.0.0",

 

 


@Maz since that error seems to be originating inside the node_modules folder, it’s not like it’s some code you can change.  As you were getting at, your issue is probably either with version incompatibility or some kind of project settings.

I’m on next ^14.2.5, and docusign-esign ^7.0.3  not sure why I’m on a much earlier version of docusign than you… did you perhaps force an installation to ignore peer dependencies?  maybe just `npm reinstall docusign-esign` and keep an eye out for warnings?

the other thing to look at is probably your ts-config.json.  Could be a setting in there interfering with how modules are resolved.  I’m no tsconfig expert but I can post mine in case it helps?

 

{
"compilerOptions": {
"target": "es5",
"lib": 5"dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": ".",
"paths": {
"@/*": {"./*"]
},
"incremental": true,
"plugins": b
{
"name": "next"
}
]
},
"include": ,"next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ,"node_modules"]
}

 


@kth Thank you for the incredibly useful code!

I had a few other issues that I needed to work out to get it to render. First, I was getting the error that "Server Functions cannot be called during initial render”, so I added a `ready` state and set it to true inside a `useEffect`.

Then I was having issues with the async createEmbeddedFormFromTemplate taking awhile to respond, and so the page keeps calling the server action continuously. Adding a `loading.tsx` to the page helped, although I still see it making five separate envelopes in Docusign before rendering.

Any ideas how to speed this up? Or at least make Nextjs actually wait for the async call to complete before triggering a re-render?


@kth Thank you for the incredibly useful code!

I had a few other issues that I needed to work out to get it to render. First, I was getting the error that "Server Functions cannot be called during initial render”, so I added a `ready` state and set it to true inside a `useEffect`.

Then I was having issues with the async createEmbeddedFormFromTemplate taking awhile to respond, and so the page keeps calling the server action continuously. Adding a `loading.tsx` to the page helped, although I still see it making five separate envelopes in Docusign before rendering.

Any ideas how to speed this up? Or at least make Nextjs actually wait for the async call to complete before triggering a re-render?

Honestly I don’t think all that was necessary.  That code should work just fine when called in a server rendering context.  Calling server actions in a server component is totally ok, no useEffect necessary.

 

In short, try adding

"use server"

to the top of //components/SignDocument.tsx and it should just work


Reply