Hello World of Contracts

Covers the basics of Ironclad's API with a simple example of storing all signed contracts to a custom datastore.

Ironclad Workflows help manage contracts, from request, to negotiation, to execution. While Ironclad's out-of-the-box integrations with cloud storage providers keep contract documents in sync automatically, you may want to use the API to persist signed agreements to an internal system, or customize where it is stored.

In this guide, we will go over:

  • Setting up API tokens
  • Configuring a webhook in the UI
  • Querying Ironclad's Workflow APIs to retrieve signed contract PDFs

You will need:

  • An Ironclad user account, with access to a simple workflow and API (Power User) access.
  • A development environment and a system for receiving webhooks, like ngrok.

Note: This guide and any software contained in it should be used at your own risk by individuals qualified to evaluate its effectiveness. IT IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL IRONCLAD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH YOUR USE OF THIS GUIDE OR ANY SOFTWARE IT CONTAINS.

Your use of the Ironclad API must comply with Ironclad’s API Terms of Use (available at https://legal.ironcladapp.com/api-terms-of-use) and the terms of the Enterprise Services Agreement (or equivalent) entered into between you and Ironclad.

Setting up API Tokens

Log into Ironclad, and navigate to the API configuration page, which will be in the dropdown menu in the top-right. From this page, you can create a new API token. Copy and save the token, since you will not be able to retrieve this token later.

Configuring a Webhook

Start by setting up a simple application endpoint. The example below uses Node.js with Express.

const express = require('express');

const app = express();

app.use(express.json());

app.post('/webhook', (request, response) => {
  let payload;

  try {
    payload = request.body.payload;
  } catch (err) {
    response.sendStatus(400);
  }

  // Get the type of event that triggered this webhook.
  const event = payload.event;
  // Get the ID of the workflow that updated.
  const workflowID = payload.workflowID;

  switch (event) {
    case 'workflow_launched':
      console.log(`launched workflow: ${workflowID}`);
      break;
    case 'workflow_completed':
      console.log(`completed workflow: ${workflowID}`);
      break;
    default:
      console.log(`received event type: ${event}`);
  }

  // Acknowledge receipt of the webhook.
  response.sendStatus(200);
});

app.listen(3000, () => console.log('Listening for webhook on port 3000'));

Once you have your simple endpoint running locally, set up a tunnel with a tool like ngrok, and direct it to port 3000. (With ngrok, you would run ngrok http 3000.)

From the Ironclad API page, click "Create Webhook," and copy and paste the tunnel URL into the "Target HTTP URL" field (adding /webhook to it, so that it looks something like http://abcde.ngrok.io/webhook).

Adding a webhook in the UI.

Launch a workflow, and you should see a simple webhook event.

Note that webhooks can be configured directly via the API, and supports verification via signatures included in the header.

Querying for Signed PDF

Now that Ironclad is pushing webhook events to our application, let's query for workflow information from the workflow API. With the code below, we request attribute information from the workflow, and if the signed document is present, we download it.

require('dotenv').config();
const fs = require('fs');
const express = require('express');
const axios = require('axios').default;

const app = express();

// Be sure to store your API securely!
const apiKey = process.env.API_KEY;

// Your host URL may vary based on the implementation.
const hostUrl = (process.env.HOST_URL ? process.env.HOST_URL : 'ironcladapp');
const apiUrl = `https://${hostUrl}.com/public/api/v1`;

app.use(express.json());

app.post('/webhook', async (request, response) => {
  let payload;

  try {
    payload = request.body.payload;
  } catch (err) {
    return response.sendStatus(400);
  }

  // Retrieve workflow information through GET endpoint
  const workflowID = payload.workflowID;
  const workflowResponse = await axios.get(
    `${apiUrl}/workflows/${workflowID}`, {
      headers: {
        Authorization: `Bearer ${apiKey}`,
      },
    },
  );

  const {data} = workflowResponse;

  // If the signed agreement is present, retrieve it and store it.
  if (data.attributes.signed) {
    const signedDocResponse = await axios.get(
      `https://${hostUrl}.com${data.attributes.signed.download}`,
      {
        headers: {
          Authorization: `Bearer ${apiKey}`,
        },
        responseType: 'stream',
      },
    );

    signedDocResponse.data.pipe(
      fs.createWriteStream(`contract-${workflowID}.pdf`),
    );
    console.log(`copied signed document for workflow: ${data.title}`);
  } else {
    console.log(`update from in-progress workflow: ${data.title}`);
  }

  // Acknowledge receipt of the webhook.
  response.sendStatus(200);
});

app.listen(3000, () => console.log('Listening for webhook on port 3000'));