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:

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

Workflows support:
- Any number of webforms 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
- Using the GraphQL API and fetching your organization and related objects
- Going through the Workflow and noting the slugs used by Anvil

Query parameters
test
- pass?test=true
to create a test item. Test items will use demo signatures and will show on your dashboard underTest
mode.d
- pass a?d=${jsonData}
query param to seed a Workflow.jsonData
is in{key: value}
form, wherekey
corresponds to a web form Field alias andvalue
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 theencryptRSA(publicKey, stringifiedJSON)
func from our encryption lib, or a raw JSON string.- Ensure
jsonData
is URL encoded. You can pass it through JavaScript'sencodeURIComponent
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 language-specific 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. ACast
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. AForge
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 moreForge
s with zero or moreCast
s. AWeld
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 aForge
. ASubmission
holds the data for one form submission on a particularForge
.WeldData
: An instance of aWeld
. It contains theSubmission
s for all theForge
s in aWeld
. When we say 'Workflow submission', we meanWeldData
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 Forge
s and three Cast
s. When the user completes the Workflow, there will be a WeldData
with 2 Submission
s: 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 {eidorganizations {eidnameslugcasts {eidname}welds {eidtitleforges {eidname}}}}}
Workflow API information
For much of the following document, you will need to know IDs of your organization, your Workflow (weld), webforms (forges) within the Workflow, etc. Each Workflow has an API Information page to help you in building out your integration. Access the API information page from the gear on your Workflow's dashboard page:

The API information page will show you all the IDs you will need to start the Workflow, submit data to the webforms, and more.

Submitting data
The most common uses of the API with Workflows are:
- Starting a Workflow seeded with data from your system
- 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:
- Starting a Workflow. Use
forgeSubmit
to create a brand newWeldData
andSubmission
on a givenWeld
. e.g. - Submitting data to an existing
Submission
. After a Workflow has been started, you can submit data to theSubmission
, adding data to thepayload
or overwriting data already in thepayload
. - Continuing a Workflow. When there are multiple web forms that need to be filled to complete a Workflow,
forgeSubmit
can create aSubmission
for an unstartedForge
.
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,) {eidweldData {eidcontinueURL}forge {eidslug}}}
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 belowname: '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
Starting a Workflow in test mode
Test mode submissions can be created via the API by setting the isTest
variable to true
.
- Test Workflow submissions are free and will not count towards your plan's Workflow submission quota.
- PDFs filled and signed in a test Workflow submission will contain a watermark and have red signatures.
- Once a Workflow submission is in test mode, it cannot be converted to live. Similarly, a live Workflow submission cannot be converted to test mode.
const result = await anvilClient.forgeSubmit({variables: {forgeEid: forge.eid,isTest: true, // The submission will be in test modepayload: { ... },},})const newSubmission = result.data.data.forgeSubmit
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 updateemail: '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 Forge
s, 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 earlierpayload: {},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":

Then set it to your key of choice

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:
## StringsBasic Text: <String>Long Text: <String>Numeric Text: <String> with 0-9 characters onlyEmail: <String> with an `@` and a valid domainSocial Security Number: <String> in format `123121234`Tax ID Number (EIN): <String> in format `921234567`## DatesDate: <String> in the format `YYYY-MM-DD`Date Dropdowns: <String> in the format `YYYY-MM-DD`## DropdownsDropdown: <String> name or ID of the option selectedDropdown (Old Style): <String> name or ID of the option selected## NumbersDecimal Number: <Number>Dollar: <Number>Integer: <Number>## BoolsCheckbox: <Boolean>## Complex typesPhone: 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}## ArraysArrays will have `n` Objects and will contain the types aboveArray: <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) {eidisTeststatus # See the WeldData object reference for all possible valuescontinueURL # The next step URL. Use this after starting the WorkflowdisplayTitle # Title shown in the UInumberRemainingSignersdataUpdatedAt# submissions on each web formsubmissions {eidstatus # See the Submission object reference for all possible valuesresolvedPayload # Data submitted by the user with related form-field dataforge {eidnameslug}}# A DocumentGroup will only be available if the weldData# has been sent for signatures, or totally completeddocumentGroup {eidstatus # See the DocumentGroup reference for all possible valuesdownloadZipURLcurrentRoutingStepsigners {eidstatusnameroutingOrder}}}}
See our Postman example for more info.
All weldData
s on a weld
To get a listing of all WeldData
s on a Weld
, use the use the weld
query:
query WeldQuery($eid: String!, $pageNum: Int) {weld(eid: $eid) {eidslugnameforges {eidslugname}weldDatas(offset: $pageNum) {rowCountpageCountpagepageSizeitems {eidisTeststatusdataUpdatedAtdisplayTitlenumberRemainingSigners}}}}
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:
weldCreate
- called when a Workflow is created.weldComplete
- called when aweldData
is completed.signerComplete
- called when a signer finishes signing.
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.
Download authentication
For both download routes, authenticate by using your API key in the Authorization
HTTP header as outlined in the Authentication section of the getting started docs.
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',}],//....}}
We provide language-specific API clients that wrap authentication and make using our API easier.
Additionally, our language-specific API clients come with functions to download documents.
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) {eidfiles}}
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:
<iframesrc="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:
- Embed a UI URL
- Create a Workflow submission via
forgeSubmit
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
.
You can enable production Workflow embedding from the API tab in your organization's settings.

Once you enable embedding, you will be asked to add your domains to a whitelist:

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',// organizationSlug: 'my-org'// weldDataEid: 'bdwIX5TUiLBzldgdwzL2',// forgeEid: 'wIX5TUiLBzldgdwzL2bd',// submissionEid: 'X5TUiLBzldgdwzL2bdwI',// pageId: 'somePageId',// }//// OR//// {// action: 'forgeComplete',// organizationSlug: 'my-org'// weldDataEid: 'bdwIX5TUiLBzldgdwzL2',// forgeEid: 'wIX5TUiLBzldgdwzL2bd',// submissionEid: 'X5TUiLBzldgdwzL2bdwI',// }})
White labeling with custom stylesheet
You can specify a custom stylesheet injected into the signature page for all signers. This stylesheet can be served from your website giving you full control over the custom styles.
White labeling with custom stylesheets is an enterprise feature. Please contact sales and let them know your use case.
Once enabled for your organization, you can set a custom stylesheet URL on a per-Workflow basis in each Workflow's settings:
