Embedding workflow web forms in your app
Tags: webdev, javascript, workflow
Published: 2021-07-22 18:52:30

A central feature of Anvil is [workflows](/workflows/). Workflows allow you to collect data from your users via a simple web form, automatically fill PDFs with the user's data, then gather signatures from relevant people.

For example, say we wanted our client to sign an NDA. We can give our client an easy webform to fill:

![workflow webform nda example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o4tcvemr3vksrpfxr6wg.png)

When the client is finished filling the webform, it will fill the NDA with their information, then ask them to sign:

![embedded workflow e signature](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dardhkam00dgg0lys2o1.png)

You can embed this entire process in an `iframe` on your app or website. That way, you control the experience, and a user does not need to leave your site to fill and sign our hypothetical NDA.

In this post, I'll show you how to

* Embed the web form filling and signature UIs in your app or website
* Start workflows
* Have your app notified when actions take place. For example, when the user submits a page, or completes the workflow
* Download the documents when the user is finished

_Note: please contact us at [support@useanvil.com](mailto:support@useanvil.com) to enable form embedding for your Anvil organization._

## Embedding the form in an iframe

Embedding the web form UI in an `iframe` is extremely easy. All you need to do is point the `src` attribute at your workflow's web form URL.

```html
<iframe
  src="https://app.useanvil.com/form/demo/client-nda/nA1ORcANs4MXAQ83KsdY"
></iframe>
```

And you will see the form inside your own app:

![workflow webform nda example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ee892gnk20bnukrk9fk5.png)

## Starting a workflow

There are two ways to start a workflow submission for one of your users:

1. Embedding a special URL to kick off a new submission
2. Creating a workflow submission via the API

### Option 1: Embed a special URL

Embedding a form URL without an ID will create a new workflow submission (a [`WeldData`](/docs/api/getting-started/#workflow-objects)). This approach is the easiest way to get a workflow submission started and seeded with your own data.

In our case, our iframe URL would look like this:

```html
<iframe
  src='https://app.useanvil.com/form/demo/client-nda?d={"name":"Sally Jones"}'
></iframe>
```

Note the `d` param, it will seed the new workflow submission with data from your system. See the [workflow UI URLs](/docs/api/form-urls/) docs for all the options available.

### Option 2: Create a workflow submission via the API

An alternative to creating a workflow submission with a special URL is starting the workflow with the GraphQL API, then embedding the resultant form URL. This approach is a little more complex, but gives you full control over the submission.

Often customers with complex workflows use the API method to start workflows because it allows for more flexibility. With the API method, you can specify a specific `webhookURL` for the workflow submission, seed multiple forms in a workflow, and more.

To create a workflow submission, use the `forgeSubmit` mutation. Our [node API client](https://github.com/anvilco/node-anvil) supports the `forgeSubmit` mutation out of the box:

```js
// Uses the Anvil client, see https://github.com/anvilco/node-anvil
const result = await anvilClient.forgeSubmit({
  variables: {
    forgeEid: forge.eid, // your web form's eid
    complete: false,
    isTest: false,
    // Seed the submission with data here...
    payload: {
      name: Sally Jones,
    },
  },
})

// You will embed the resulting `weldData.continueURL` in the iframe
const responseData = result.data.data.forgeSubmit
const embedURL = responseData.weldData.continueURL
```

Then in the HTML, you would use `embedURL` in the `iframe`'s `src` attribute:

```jsx
<iframe
  src={embedURL}
></iframe>
```

See the [`forgeSubmit`](/docs/api/graphql/#forgesubmit) docs for more information on using `forgeSubmit`. The basic mutation call used above to start a workflow is as follows:

```js
mutation ForgeSubmit(
  $forgeEid: String!,
  $weldDataEid: String,
  $submissionEid: String,
  $payload: JSON!,
  $complete: Boolean,
  $isTest: Boolean,
  $webhookURL: String,
) {
  forgeSubmit (
    forgeEid: $forgeEid,
    weldDataEid: $weldDataEid,
    submissionEid: $submissionEid,
    payload: $payload,
    complete: $complete,
    isTest: $isTest,
    webhookURL: $webhookURL,
  ) {
    eid
    weldData {
      eid
      continueURL
    }
  }
}
```

## Getting notified

Your app can be notified when a user takes an action in two ways:

1. JavaScript events from the `iframe` to the parent frame
2. Webhook notifications

### Events from the iframe

For some actions, the `iframe` will emit events that your parent frame can capture. These events will help you know when the user is finished filling a page, finished with the webform, and finished signing.

On the page that renders the `iframe` element, you can subscribe to the `window`'s `message` event. The events emitted by the iframe should provide you with any ids you need to query our system for the user's submitted data.

```js
window.addEventListener('message', ({ origin, data: messagePayload }) => {
  if (origin !== 'https://app.useanvil.com') return
  // messagePayload will be an object in one of the formats
  //
  // {
  //   action: 'forgeSubmitPage',
  //   organizationEid: 'zL4bdwIX5TUiRb26dgdw'
  //   weldDataEid: 'bdwIX5TUiLBzldgdwzL2',
  //   forgeEid: 'wIX5TUiLBzldgdwzL2bd',
  //   submissionEid: 'X5TUiLBzldgdwzL2bdwI',
  //   pageId: 'somePageId',
  // }
  //
  // OR
  //
  // {
  //   action: 'forgeComplete',
  //   organizationEid: 'zL4bdwIX5TUiRb26dgdw'
  //   weldDataEid: 'bdwIX5TUiLBzldgdwzL2',
  //   forgeEid: 'wIX5TUiLBzldgdwzL2bd',
  //   submissionEid: 'X5TUiLBzldgdwzL2bdwI',
  // }
})
```

### Webhook notifications

On the server side, you can receive webhook notifications for several actions&mdash;Anvil will POST to an endpoint on your server. You can set up a global webhook URL in your [organization's settings](/docs/api/webhooks/), or you can set a [per-object webhook URL](/docs/api/webhooks/#per-object-webhook-urls) for each workflow submission. In either case, your webhook URLs will receive the same event payloads.

The most important actions for workflow embedding are:

* [`forgeComplete`](/docs/api/webhooks/#forgeComplete) - A user completes a webform
* [`signerComplete`](/docs/api/webhooks/#signercomplete) - A signer finishes signing all of their documents
* [`weldComplete`](/docs/api/webhooks/#weldcomplete) - The workflow submission is completely finished, all web forms have been completed, and all signers have finished signing

See the [webhooks guide](/docs/api/webhooks/) for more information on handling webhooks.

## Downloading the documents

When all forms have been filled, and all signers have signed, it's time to download the documents. Download them in zip format with the following URL:

```
http://localhost:3000/download/:weldDataEid.zip
```

The best way to know when everything is finished is by way of the [`weldComplete`](/docs/api/webhooks/#weldcomplete) webhook notification mentioned above. We recommend you download the zip in that webhook notification response handler.

Your specified webhook URL will be called with a JSON payload that contains the download URL. The webhook payload:

```js
// Zip download URL is at `data.documents.url`
{
  action: 'weldComplete',
  token: '38Gp2vP47zdj2WbP1sWdkO2pA7ySmjBk'
  data: {
    eid: 'GHEJsCVWsR1vtCx3WtUI',
    documents: [{
      type: 'application/zip',
      url: 'http://localhost:3000/download/GHEJsCVWsR1vtCx3WtUI.zip',
    }],
  },
  // ... other data
}
```

Then you can request the file at `request.data.documents[0].url`:

```js
const downloadURL = webhookRequest.jsonBody.data.documents[0].url
const zipResponse = httpFetch(downloadURL, options)
```

If you are using our node API client, this is even easier with the [`downloadDocuments()`](https://github.com/anvilco/node-anvil#downloaddocumentsdocumentgroupeid-options) function

```js
const weldDataEid = request.data.eid
const zipResponse = await anvilClient.downloadDocuments(weldDataEid)
fs.writeFileSync('workflowDocuments.zip', zipResponse.data)
```

## All done!

That's it! Now you have all the tools to embed a workflow's form in your app or website, get notified when something happens, and download the finished documents.

Note that if you are already collecting your client's information with your own web forms, you can use [Etch e-sign](/docs/api/e-signatures/) to fill PDFs with your data, create signature packets, and embed the e-signing UI in your app.

If you have questions, or want to enable workflow embedding in your app, please do not hesitate to contact us at:
[support@useanvil.com](mailto:support@useanvil.com)