Webhooks

If you set up webhook endpoints in your developer settings, your endpoints will receive a POST request with an event object whenever a form is completed.


The event object
{
  "id": "EMcUHi5K81vBcBSQafx9P3",
  "type": "form.created",
  "form": {
    "id": "RCBrLQKLYAuMg5k9UGCSxC",
    "reference": "ab0b12e2-979c-47be-97d8-acf614685d57",
    "type": "w9Oct2018",
    "data": {},
    "signatures": []
  }
}

Event properties

id string

A unique identifier for the event


type string

A string indicating the type of event. Currently the only event that is sent via webhook is the type form.created.


form object

A form object. See the form object reference for more information.



Verify the webhook signature (recommended)

Nextform signs requests with a Nextform-Signature header which includes a timestamp and HMAC hash. This allows you to optionally verify that the events were sent by Nextform, not by a third party. Here is a sample implementation for secure webhook handling with Express and Node.js:

// Import crypto functions along with your other usual imports
const { createHmac, timingSafeEqual } = require('crypto')

router.post('/webhook', async (req, res, next) => {
  try {
    // The signature is delivered in the format t=1492774577,s=5257a869e7ecebe...
    // where t= is the timestamp and s= is the signature hash

    // This helper function gets the timestamp and hash from the header
    function parseSignature(signature) {
      if (typeof signature !== 'string') return [null, null]
      const values = signature.split(',')
      const timestamp = values.find((s) => s.startsWith('t='))?.replace('t=', '')
      const hash = values.find((s) => s.startsWith('s='))?.replace('s=', '')
      return [timestamp, hash]
    }

    // Get the timestamp and hash. You may want to add handling for null/undefined values
    const [timestamp, hash] = parseSignature(req.get('Nextform-Signature'))

    // Now recalculate the signature by joining the timestamp and request body with a dot
    const payload = timestamp + '.' + JSON.stringify(req.body)
    const recalculatedHash = createHmac('sha256', SECRET).update(payload).digest('hex')

    // Verify the signature use a timing safe comparison algorithm
    const isTrusted = timingSafeEqual(Buffer.from(hash), Buffer.from(recalculatedHash))
    if (!isTrusted) return res.status(401).send('Invalid signature.')

    // Add your code to handle the data from the webhook and send a 200 OK response

    res.end()
  } catch (err) {
    next(err)
  }
})

If a 2XX or 3XX response is recieved from your server, the event will be considered successfully delivered. If no response or a bad response is received, delivery will be retried for up to 24 hours with an exponential back off.