Automate eSignature and document workflows for purchase orders: A developer’s guide

Automate eSignature and document workflows for purchase orders: A developer’s guide

At many companies, it’s common to have the ability to generate purchase orders from data housed in your CRM, ERP, or backend systems. What’s missing is automating the process of getting those documents signed and approved following your company’s business practices.

You need a solution that offers seamless integration with your existing systems and is easy to customize to match your company’s unique business processes. Efficiency in implementation is a plus; the quicker you can set up and automate your processes, the better.

Electronic signature security and compliance

The solution also needs to comply with eSignature  laws and regulations and ensure data security to protect confidential documents and information.

eSignature: E-Sign Act, UETA

Data Privacy: GDPR, CPRA, HIPAA-ready

Data Security: SOC 2 Type II, SHA-256 encryption

How to dynamically generate purchase orders and embed eSignature in your app

In this tutorial, we’ll demonstrate how to generate professional documents populated with backend data using Document Automation Hub. We’ll show how to conditionally display terms and conditions based on region, add signature, and seamlessly integrate document workflow and eSignature inside your Next.js or React applications.

Purchase Order app

A basic React app simulating data retrieval from an existing system.

Purchase Order document

A DOCX file, that will be used to dynamically generate the purchase order with the provided data. It will conditionally display terms and conditions based on the selected region.

Embedded signing

Embed eSignature in your application, website or CRM.

Setup: Download the code

We’ll use a Next.js app to simulate data retrieval from your backend systems by passing data in via an HTML form. Download the code here or clone it to a local repository.

git clone

Key concepts

We’ll use Document Automation Hub, an API for automating document workflows, to dynamically pre-fill the document, conditionally display terms and conditions, and generate a link for document signing.

OrganizationThe environment (or workspace) where you manage Templates and Flows, upload and create documents and forms, organize webhook events, and users.
TemplateA workflow configuration containing one or more documents or forms.
FlowAn actionable copy of all documents and forms in a Template.

Authentication: JWT Grant

  1. Create a free developer account
  2. Log into the Automation Hub Dashboard
  3. Click Add Application, and enter:
Application namePurchase Order App
Application URL

Generate JWT Token

  1. Click on the application you just created → under Recent Applications
  2. Click JWT Token
  3. Create RSA → Copy your private key
  4. Click “Generate token” → Paste your Private key and set the Expiration date

Copy the JSON Web Token → We’ll need this to make our API calls.

Document generation

Document generation gives you the ability to dynamically generate PDFs from Microsoft Word documents by adding custom variables that are later populated with specific data.

Automating this step can add time savings, accuracy and consistency to your documents. And using a Word document gives you full control over branding so you can exactly match your business requirements and design.

Dynamic content

Our purchase order contains text tags, conditionals, and a signature field.

Text Tags{{Client_Name}}
{{City}}, {{State}} {{Zip}}
Custom variables we’ll use to pre-fill our document with data we pass in
Add conditional logic to generate different terms and conditions per region
Fillable Fields{{t:s;r:n;l:”Signature_1″;}}To collect user input, like a signature field

See “IT Purchase Order.docx” in the root directory of the project to see how it works, or manually add them to your own Word document to get hands-on experience.

Tip: Use straight quotes (not curly quotes!) when adding the signature field.

Base64 encode

To prepare the document(s) for upload, we need to encode them in Base64 format.

For simplicity, “IT Purchase Order.docx” has already been encoded as “IT Purchase Order.docx.base64”.

To Base64 encode your documents:

  1. Use a free online Base64 encoder.
  2. Use encodeDocxToBase64.js to encode your documents on your local machine.

node encodeDocxToBase64.js <path-to-file.docx>

Note: You can find “IT Purchase Order.docx.base64” and “encodeDocxToBase64.js” in the root directory of the project.

Create a Template

Next, let’s create the Template. A template represents the workflow configuration, documents and forms for your workflow.

Let’s take a look at the curl request example from the docs to see how it works:

curl --request POST \
  --url \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer undefined' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "My Template",
    "description": "Template for HR Office",
   "redirect_url": ""
…/v1/organizations/organization_id/templatesAPI endpoint: Requires “organization_id”
Accept: application/jsonResponse in JSON format
Authorization: Bearer undefinedAuthentication
Content-TypeRequest in JSON format
data ‘{
“name”: “My Template”,
“description”: “Template for HR Office”,
“redirect_url”: “”
Template name, description, and redirect_url

Organization ID

To get the organization ID, let’s take a look at the curl request example from the docs:

curl --request GET \
  --url \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer undefined'

This call gets all organizations we belong to. We’re also going to take care of a few housekeeping items here to simplify the rest of our API calls.

Optional: You can log in with your free developer account to create an organization (workspace) at

Implement with Axios and Next.js

It’s straightforward to map the example curl request using axios.

  1. endpoint: url + query parameters
  2. options: headers for authentication and expected data formats
  3. payload: data; none in this case
// api.js -> API calls to
const getOrganization = async () => {
  try {
    const endpoint = `${BASE_URL}/organizations?per_page=100`;
    const response = await axios.get(endpoint, { headers: await getHeaders() });
    return => === ORGANIZATION);

. . .

// getHeaders()
const getHeaders = async () => {
  const accessToken = await getAccessToken();
  return {
    'Accept': 'application/json',
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json',

Get Access Token

We’re also going to use this initial API call to check if we have a valid access token and obtain a new one if we don’t have one or if it’s expired.

// api.js -> getOrganization (cont’d)
. . .
} catch (error) {
    if (error.response && error.response.status === 401) {
      console.warn('Unauthorized request when fetching organizations. Attempting token refresh.');

      const newToken = await obtainAccessToken();

Create a Template with Axios

Now that we have the organization ID and access token, we’ll create the template by passing in the organization ID, name, description, and redirect_url.

const TEMPLATE = "Purchase Order Template";
const REDIRECT_URL = "";

const createTemplate = async (organizationId) => {
  const endpoint = `${BASE_URL}/organizations/${organizationId}/templates`;

  const payload = {
    name: TEMPLATE,
    description: "Purchase Order Template",
    redirect_url: REDIRECT_URL

  const response = await, payload, { headers: await getHeaders() });


Upload document

After creating the template, we’ll upload one or more documents or forms. In our case, we’re uploading a Word document so we’ll set type to “DOC_GENERATION” and pass in our Base64 encoded document.

const DOCUMENT = "IT Purchase Order.docx";

const uploadDocument = async (organizationId, templateId) => {
  const endpoint = `${BASE_URL}/organizations/${organizationId}/templates/${templateId}/documents`;

  const payload = {
    name: DOCUMENT,
    type: "DOC_GENERATION",
    content: getBase64Content()

  return await, payload, { headers: await getHeaders() });

const getBase64Content = () => {
  const filePath = path.join(process.cwd(), 'IT Purchase Order.docx.base64');
  const base64Content = fs.readFileSync(filePath, 'utf-8');
  return base64Content;

Pre-Fill via API Bot

Document Automation Hub leverages airSlate to handle the embedded signing session. To configure the signing session to pre-fill our documents via the API:

  1. Log into using your free developer account
  2. Click All Templates
  3. Click Configure Template

4. Click All Bots (lower right of screen)

5. Click Add Bot
6. Search for “Pre-fill via API”
7. Click Install Bot

8. Click Publish

Create a Flow

Next, we’ll create a Flow → an actionable copy of our documents and forms. This is the step where we’ll pass in our data to dynamically generate our document.

Note: orderData contains the data from the HTML form.

const createFlow = async (organizationId, templateId, document, orderData) => {
  const endpoint = `${BASE_URL}/organizations/${organizationId}/templates/${templateId}/flows`;

  const payload = {
    documents: [
        fields: [
            "id": getFieldIdByName(document, "Order_Date"),
            "type": "date",
            "name": "Order_Date",
            "placeholder": ""
            "id": getFieldIdByName(document, "Client_Name"),
            "type": "text",
            "name": "Client_Name",
            "value": orderData.clientName,
            "placeholder": ""

  const response = await, payload, { headers: await getHeaders() });

Create a link to the Flow

Now we’re ready to send out our first Flow. The documents or forms provided in the link can only be submitted once and expires after a set duration of time (in minutes).

Note: roleId is returned in the response when we create a Flow.

// createPurchaseOrder.js
roleId = flow.available_roles[0].id;

// api.js
const distributeFlow = async (organizationId, templateId, flowId, roleId) => {
  const endpoint = `${BASE_URL}/organizations/${organizationId}/templates/${templateId}/flows/${flowId}/share`;

  const payload = {
    data: [
        auth_method: "none",
        expire: 60,
        role_id: roleId

  return await, payload, { headers: await getHeaders() });

Embed eSignature in your app

We’ll use the URL we retrieved in the last step to embed the signing session within our app.

return (
  <div style={{ width: '100%', height: '100vh' }}>
    <div className="bg-blue-600 p-5">
      <h1 className="text-2xl text-white">Technology Solutions, Inc.</h1>
        border: 'none',
        width: '100%',
        height: '100%',
        overflow: 'hidden'
      title="Embedded Docx"

Free Developer Account

Document Automation Hub is a useful addition to your toolkit with a suite of API capabilities that go beyond eSignature, and includes document workflow, dynamic webforms, embedded PDF editing, and PDF tools that easily integrate with your existing systems.

Head over to our developer page to create a free developer account and get 250 flows for free. And check out our Stack Overflow community to post questions on this demo or on how to use any of our APIs. We regularly check in on questions asked by developers there.

Document Automation Hub docs:

Happy coding! We hope this article helps you automate document workflows and eSignature for your purchase orders.

Access every workflow automation API you need to transform users’ experience.