Workflow API usage

Introduction

A central feature of Anvil is 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, either with Anvil's own Etch e-signatures, or with your own DocuSign account.

For example, say you wanted a client to sign an NDA. You can give the client an easy webform to fill:

workflow webform nda example

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

Workflows support:

  • Any number of web forms for collecting user information
  • Filling any number of PDFs when all webforms have been completed
  • Sending filled documents out for signature to any number of signers
  • Logic: show and hide web form fields, web form pages, turn on and off PDFs, and conditionally fill fields based on data entered by the user
  • Webhooks: get notified when actions are taken by your users
  • Embedding: embed web forms and signing UIs in your app or website

This guide will cover the most common ways of interacting with workflows via the API. Some things to keep in mind:

  • Anything the Anvil UI can do, and any data the UI shows is available to you via the API. If you want to do something that is not covered in this guide, see the GraphQL reference, or contact us at support@useanvil.com.
  • You can programmatically control the entire workflow process: who gets forms when, and who signs when.

Don't need to collect data from users via web forms? Only need e-signatures? See our Etch e-sign API guide. Etch e-sign allows you to do everything a workflow can do, without web forms.

No code: UI URLs

The simplest way of interacting with a workflow is starting it by way of specially crafted URL. You can kick off a workflow submission and seed it with data without an API key.

Start Workflow: /form/:org/:forge

Kick off a workflow with this URL. You can construct this URL and send it to a client to use in their browser. This is a simple way to integrate a workflow into your app: create these URLs and link your users to them in your app.

https://app.useanvil.com/form/${organization.slug}/${forge.slug}?d={...}

When the user hits the link in their browser, Anvil will create a Submission and WeldData, then redirect them to the submission URL:

https://app.useanvil.com/form/myorg/admin-onboarding/${submissionEid}

Finding URL slugs

Find the slugs for the URL either by

  1. Using the GraphQL API and fetching your organization and related objects
  2. Going through the workflow and noting the slugs used by Anvil

URL slugs in url

Query parameters

  • test - pass ?test=true to create a test item. Test items will use demo signatures and will show on your dashboard under Test mode.
  • d - pass a ?d=${jsonData} query param to seed a workflow.
    • jsonData is in {key: value} form, where key corresponds to a web form Field alias and value is the data you want to populate the field. See the payload format section of this document for full details on acceptable formats.
    • jsonData fields will be validated according to the web form field type. Fields that do not pass validation will be omitted and not show in the resulting workflow submission.
    • jsonData can be encrypted via the encryptRSA(publicKey, stringifiedJSON) func from our encryption lib, or a raw JSON string.
    • Ensure jsonData is URL encoded. You can pass it through JavaScript's encodeURIComponent or an equivalent function in your language of choice.

Generating the d query parameter

Here is a quick Node script generating a d param:

import { encryptRSA } from '@anvilco/encryption'
const publicKey = `-----BEGIN PUBLIC KEY-----
add yours here
-----END PUBLIC KEY-----`
function buildURL (path, dParam) {
return `https://app.useanvil.com/form/my-org/my-form?d=${encodeURIComponent(dParam)}`
}
const exampleData = {
name: 'Sally Jones',
}
const dParam = JSON.stringify(exampleData)
const encryptedDParam = encryptRSA(publicKey, dParam)
const url = buildURL(formURL, dParam)
const encryptURL = buildURL(formURL, encryptedDParam)
console.log(url)
console.log(encryptURL)

API Authentication

Interacting with workflows via the API will be mostly through GraphQL queries and mutations. To access the API, the first thing you'll need to do is grab your API keys from your Organization Settings -> API Settings page. See the getting started guide for detailed steps to get your keys and authenticate to the API.

We provide a node and python API clients that wrap authentication and make using our API easier.

Postman collection

We've created a Postman collection showing authentication and usage of a number of endpoints and GraphQL queries.

Terminology

In order to effectively use the API, you'll need to get a handle on our terminology. See a comprehensive list in the terminology section of the Getting Started guide.

For workflows, there are five main objects you need to understand. First, there are three that hold configuration information: Cast, Forge, and Weld. You can think of them as classes in OOP parlance.

  • Cast: Configuration for a single PDF template. A Cast defines the location of boxes on PDF pages, and the type of each box (e.g. date, phone number, etc.).
  • Forge: Configuration for a single web form. A Forge defines a web form's fields, the page fields are on, their types, web form logic, etc.
  • Weld: A Weld is a Workflow. It connects one or more Forges with zero or more Casts. A Weld basically glues (ahem, welds) together the other objects to create the configuration for a single workflow.

As the previous objects are OOP classes, there are instances of these objects:

  • Submission: An instance of a Forge. A Submission holds the data for one form submission on a particular Forge.
  • WeldData: An instance of a Weld. It contains the Submissions for all the Forges in a Weld. When we say 'workflow submission', we mean WeldData

For example, say you have one workflow that has two webforms and fills three PDFs.

In this case, there will be one Weld with two Forges and three Casts. When the user completes the workflow, there will be a WeldData with 2 Submissions: one Submission for each Forge. Additionally, there will be one DocumentGroup that is a zip file containing three PDFs.

Your first query

A great place to start playing with the API is the currentUser query. From here, you can view all the objects on your organization. For example, this query fetches casts (PDF templates), welds (workflows), and forges(web forms):

POST https://graphql.useanvil.com
{
currentUser {
eid
organizations {
eid
name
slug
casts {
eid
name
}
welds {
eid
title
forges {
eid
name
}
}
}
}
}

Submitting data

The most common uses of the API with workflows are

  1. Starting a workflow seeded with data from your system
  2. Updating data in an existing submission

For example, you already have your user's name and email address, but want them to complete a few pieces of missing info. So you send them a web form with their name and email pre-filled. So you'd start the workflow by passing the user's name and email address. At a later date, you may want to update, say, their address.

The forgeSubmit mutation is the centerpiece of the processes described above. It allows you to kick off workflows, and update data for existing submissions. You only need forgeSubmit if you want total programmatic control. A simpler way to start workflows is with our UI URLs described at the top of this guide.

Any time data is submitted to a workflow, it flows through forgeSubmit. There are a couple situations forgeSubmit handles:

  1. Starting a workflow. Use forgeSubmit to create a brand new WeldData and Submission on a given Weld. e.g.
  2. Submitting data to an existing Submission. After a workflow has been started, you can submit data to the Submission, adding data to the payload or overwriting data already in the payload.
  3. Continuing a workflow. When there are multiple web forms that need to be filled to complete a workflow, forgeSubmit can create a Submission for an unstarted Forge.
forgeSubmit (
forgeEid: String!,
weldDataEid: String,
submissionEid: String,
payload: JSON!,
currentStep: Int,
complete: Boolean,
isTest: Boolean=false,
timezone: String,
webhookURL: String,
): Submission

Starting a workflow

Starting a workflow entails passing a forgeEid and an optional payload. A Forge is a web form, so you are seeding the web form described by the forgeEid with your data in payload.

forgeSubmit will return a new Submission on the specified Forge with a new WeldData.

Here's the mutation:

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
}
forge {
eid
slug
}
}
}

And an example with the Node API client:

const result = await anvilClient.forgeSubmit({
variables: {
forgeEid: forge.eid,
complete: false,
isTest: false,
// Seed the submission with data here...
payload: {
// See field aliases section below
name: 'Sally Jones',
email: 'sally@example.com',
},
// optional webhook URL for actions taken on the new
// WeldData, Submission, and Signers. You must first
// enable webhooks.
webhookURL: 'https://mysite.com/anvil-webhook',
},
})
const newSubmission = result.data.data.forgeSubmit
// => {
// eid: 'abc123'
// payload: {} // the actual data
// weldData: {
// eid: 'def456',
// continueURL: 'https://app.useanvil.com/form/demo/my-form/abc123'
// }
// forge: {
// eid: 'ghi789',
// slug: 'my-form'
// }
// }
// UI URL for this Forge Submission is
// https://app.useanvil.com/form/${organization.slug}/${forge.slug}/abc123

Updating a submission

Submitting data to the created submissions requires the forgeEid, weldDataEid, and submissionEid. You only need to specify the payload information you wish to update. If there is data at the key specified, it will be overwritten.

forgeSubmit({
variables: {
forgeEid: forge.eid,
weldDataEid: subimssion.weldData.eid,
submissionEid: subimssion.eid,
payload: {
// You only need to specify the data you want to update
email: 'bobby@tables.com'
},
complete: false,
},
})

Continuing a workflow

Say you have a workflow with 2 webforms: Admin Form and Client Form. There will be two Forges, one for Admin Form and one for Client Form. In order to complete the workflow, there needs to be one WeldData with two completed Submissions, one Submission for the Admin Form Forge, and one for the Client Form Forge.

Often in this situation, there will already be a WeldData and one Submission, the other Submission just needs to be created. Create the other submission this way:

forgeSubmit({
variables: {
forgeEid: clientForge.eid,
weldDataEid: subimssion.weldData.eid, // the weldData.eid that was created earlier
payload: {},
complete: false,
isTest: false,
},
})
// => submission: {
// eid: 'abcdef'
// payload: {} // the actual data
// weldData: {
// eid: 'def456'
// }
// }
// UI URL for this Forge Submission is
// https://app.useanvil.com/form/${organization.slug}/${clientForge.slug}/abcdef

Look through our example in code on Postman here.

Payload format

In the last few examples, there's been a payload object. For example:

forgeSubmit({
variables: {
// ...
payload: {
email: 'bobby@tables.com'
},
},
})

Generally the payload object format is

{
<fieldAliasOfFormField>: <valueToSetInFormField>,
<fieldAliasOfFormField>: <valueToSetInFormField>,
// ...
}

Payload keys

The keys in payload can be set "Field alias" in the web form editor. Click on your field of choice, then the "More options" button, and add a "Field alias":

Email field alias

Then set it to your key of choice

Email field alias

Payload values

Values you send in the payload object correspond to the type of web form field you are filling. See below for a comprehensive list of web form field type to their acceptable value types:

## Strings
Basic Text: <String>
Long Text: <String>
Numeric Text: <String> with 0-9 characters only
Email: <String> with an `@` and a valid domain
Social Security Number: <String> in format `123121234`
Tax ID Number (EIN): <String> in format `921234567`
## Dates
Date: <String> in the format `YYYY-MM-DD`
Date Dropdowns: <String> in the format `YYYY-MM-DD`
## Dropdowns
Dropdown: <String> name or ID of the option selected
Dropdown (Old Style): <String> name or ID of the option selected
## Numbers
Decimal Number: <Number>
Dollar: <Number>
Integer: <Number>
## Bools
Checkbox: <Boolean>
## Complex types
Phone: One of
<String> with no formatting `555113333` or `+4402012341234`
<Object> {
region: 'US',
num: '555113333'
}
Full Name: One of
<String> 'Bobby Jones'
<Object> {
firstName: 'Bobby',
mi: 'W',
lastName: 'Jones'
}
US Address: <Object> {
street1: '123 Main St',
city: 'San Francisco',
state: 'CA',
zip: '94106',
country: 'US' // ISO 3166 country code
}
## Arrays
Arrays will have `n` Objects and will contain the types above
Array: <Array> [
{
anId: ...one of the above...,
anotherId: ...one of the above...,
...
},
{...}
]

Fetching data and status info

You can fetch data and status information from an individual workflow submission (WeldData), or from a collection of them on a specific Workflow (Weld).

Individual weldData

To fetch an individual workflow submission, use the weldData query:

query weldData($eid: String!) {
weldData(eid: $eid) {
eid
isTest
status # See the WeldData object reference for all possible values
continueURL # The next step URL. Use this after starting the workflow
displayTitle # Title shown in the UI
numberRemainingSigners
dataUpdatedAt
# submissions on each web form
submissions {
eid
status # See the Submission object reference for all possible values
resolvedPayload # Data submitted by the user with related form-field data
forge {
eid
name
slug
}
}
# A DocumentGroup will only be available if the weldData
# has been sent for signatures, or totally completed
documentGroup {
eid
status # See the DocumentGroup reference for all possible values
downloadZipURL
currentRoutingStep
signers {
eid
status
name
email
routingOrder
}
}
}
}

See our Postman example for more info.

All weldDatas on a weld

To get a listing of all WeldDatas on a Weld, use the use the weld query:

query WeldQuery($eid: String!, $pageNum: Int) {
weld(eid: $eid) {
eid
slug
name
forges {
eid
slug
name
}
weldDatas (offset: $pageNum) {
rowCount
pageCount
page
pageSize
items {
eid
isTest
status
dataUpdatedAt
displayTitle
numberRemainingSigners
}
}
}
}

Check out our Postman example for more information.

Webhook notifications

You can be notified when the user takes some actions via webhooks. That is, we will POST to a URL on your server when these actions take place. You can set up two kinds of webhooks:

  • Global webhooks - actions on all workflows in your organization will call your organization webhook URL.
  • Per-object webhooks - actions on a specific WeldData will call a specific webhook URL.

Workflow-specific actions you can listen for:

See the webhooks guide for full details on their setup and handling.

Downloading documents

Documents can be downloaded in zip form, or individually. They can also be downloaded while the WeldData is still in progress. For example, you can download documents when your users are in the process of filling web forms or signing documents. Downloading in-progress documents will fill PDFs with the data available, and if only some parties have signed, documents will show signatures for those who have signed.

Most commonly, customers download the entire zip when a workflow is completely finished.

Downloading zip files

The easiest and most common way to download documents is by downloading the entire zip file via the /download/:weldDataEid.zip URL:

GET https://app.useanvil.com/download/${weldDataEid}.zip
# e.g.
# GET https://app.useanvil.com/download/GHEJsCVWsR1vtCx3WtUI.zip

If you are subscribing to webhook notifications, weldComplete actions will contain the download URL in the webhook payload:

{
action: 'weldComplete',
data: {
eid: 'GHEJsCVWsR1vtCx3WtUI',
documents: [{
type: 'application/zip',
url: 'https://app.useanvil.com/download/GHEJsCVWsR1vtCx3WtUI.zip',
}],
//....
}
}

Additionally, both our Node API client and Python API client come with downloadDocuments() and download_documents() functions respectively.

Downloading individual files

You can download individual files via the URL:

GET https://app.useanvil.com/download/${weldDataEid}/${filename}

But first, you'll need to get a file listing to know which files are available for download. To do that, you can use the files resolver on the WeldData object:

query weldData($eid: String!) {
weldData(eid: $eid) {
eid
files
}
}

Which would result in a response payload like:

{
eid: 'GHEJsCVWsR1vtCx3WtUI',
files: [{
filename: 'Hello World.pdf',
name: 'Hello World',
type: 'pdf',
}]
}

Then you can fetch the individual file:

GET https://app.useanvil.com/download/GHEJsCVWsR1vtCx3WtUI/Hello+World.pdf

Embedding workflows in your app

You can embed workflows in an iframe on your app or website. That way, you control the web form filling + signing experience, and a user does not need to leave your site.

See the embedding blog post for a workflow embedding tutorial.

Embedding the web form UI in an iframe is simple. Just point the src attribute at your workflow's web form URL:

<iframe
src="https://app.useanvil.com/form/my-org/my-form/nA1ORcANs4MXAQ83KsdY"
></iframe>

Starting an embedded workflow

You can start workflows exactly as you would without an iframe embed with either of the options described in previous sections of this document:

Enabling workflow embedding

Out of the box, you can embed test workflow submissions in your app. That is, you can embed URLs to any WeldData started via the UI URL with test=true or via forgeSubmit with isTest set to true.

We must approve your account in order for you to embed live workflow submissions. Once you are getting close to going live with your integration, please contact us at support@useanvil.com to enable form embedding for your Anvil organization.

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.

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',
// }
})
back to articles

Ready to upgrade your paperwork?

Start filling, generating, and signing PDFs from your app. Every account comes with free access to the Developer API.