Ask Anvil

Answers to questions about automating PDFs, e-signatures, Webforms, and other paperwork problems.
E-signatures
Categories

How do I automatically save signed documents to my document management system?

E-signature platforms do not file documents into your DMS for you. What they give you is a completion webhook and a download endpoint, and the integration is the twenty or so lines that connect the two. Here is the whole thing in Node, using Anvil's Etch e-sign API and S3 as the destination.

The webhook handler

const express = require('express')
const Anvil = require('@anvilco/anvil')
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3')

const anvil = new Anvil({ apiKey: process.env.ANVIL_API_KEY })
const s3 = new S3Client({ region: 'us-east-1' })
const app = express()

app.post('/webhooks/anvil', express.json(), async (req, res) => {
  const { action, token, data } = req.body

  // Anvil generates this token when you enable webhooks
  if (token !== process.env.ANVIL_WEBHOOK_TOKEN) return res.sendStatus(401)
  if (action !== 'etchPacketComplete') return res.sendStatus(204)
  if (data.isTest) return res.sendStatus(204)

  // Acknowledge first, then do the slow work
  res.sendStatus(204)

  // Fetch the completed packet as a zip
  const { statusCode, data: zipBuffer } = await anvil.downloadDocuments(
    data.documentGroup.eid
  )
  if (statusCode !== 200) throw new Error(`Download failed: ${statusCode}`)

  // File it. Keyed by packet eid, so a retry overwrites instead of duplicating.
  await s3.send(new PutObjectCommand({
    Bucket: 'signed-contracts',
    Key: `packets/${data.eid}.zip`,
    Body: zipBuffer,
    ContentType: 'application/zip',
  }))
})

app.listen(3000)

Anvil fires etchPacketComplete only after every signer has finished. The payload carries documentGroup.eid, which is the ID you hand to downloadDocuments to get the signed packet back as a zip. Swap the S3 call for a SharePoint, Box, or Google Drive upload and the shape of the handler stays the same.

What trips people up

  • Acknowledge first, download second. Anvil retries a webhook up to five times (immediately, then at roughly 5 seconds, 1 minute, 3 minutes, and 7 minutes) if your endpoint times out or returns a status of 400 or above. Downloading a large zip inside the request is the easiest way to earn yourself a duplicate delivery.
  • Make the write idempotent. Because of those retries, name the stored object after the packet eid rather than a timestamp, so a redelivery overwrites the file instead of creating a second copy of the same contract.
  • Check the token. Every Anvil webhook body includes a token that is generated when you enable webhooks in Organization Settings. Treat it like an API key, compare it in your handler, and reject anything that does not match. Anvil also calls from two fixed IPs (35.233.165.3 and 34.148.239.131) if you want to allowlist them.
  • Decrypt if you created a keypair. If your organization has an RSA keypair set up in API settings, the data field arrives encrypted instead of as plain JSON, and you have to decrypt it before you can read documentGroup.eid.
  • Skip test packets. The payload includes an isTest flag, and test signings landing in your production DMS is an easy mistake to ship.

The payload shape for every event is in the webhook documentation, and the Etch e-sign guide covers creating the packet in the first place.

Back to All Questions

The fastest way to build software for documents

Anvil Document SDK is a comprehensive toolbox for product teams launching document flows where PDF filling, signing, and complex conditional scenarios are necessary.
Explore Anvil
Anvil Webforms