This Halloween season, let us make one element of your life less spooky: webhooks! Webhooks can be pretty daunting to the average hacker. Luckily, with the correct tools, one can overcome the intimidation that webhooks may present.

‍

Background

Webhooks can be scary; they are live data feeds that allow you to edit, create and delete records through a few lines of code. But the benefits of using webhooks are extraordinary. Webhooks have the ability to provide detailed overviews of what is occurring with data in the field. These event notifications (or HTTP callbacks) can provide vital alerts if someone has accidentally deleted values or overwritten work that was previously collected. Essentially, when data changes in Fulcrum, webhooks push those changes to your servers in real-time. They enable you to analyze and integrate your data.

If you want to learn more about the benefits of webhooks over other options I recommend you spend some time reading this guide on webhooks.

‍

Overview

To get webhooks up and running, you need to write some code with instructions with what you want to do with the Fulcrum data. Once that is completed, you need to host the code up on the web, pop the URL of the hosted script in your Fulcrum account and then watch as the data changes come in.

These code blocks are going to set you up for exchanging data from one Fulcrum form to another (just so you can get a sense of both sides of the story – taking data out from Fulcrum and putting data back in).

‍

Using Node.js

There are a number of languages and platforms one can use to build and host webhooks. In this example, we will use node.js and a few npm packages (libraries). This code assumes that you have set up two forms with duplicated schemas.

In this first code block, we load up libraries. Then, the payloadProcessor() function defines the payload by REST type. If the action conducted in Fulcrum is a “create record”, then the data will go to the createRecord() function and same for updateRecords() and deleteRecords() actions.

// `express` is a minimal and flexible web application framework that helps with routing logic.var express = require('express');// `fulcrumMiddleware` is a library that lets you take some shortcuts for setting up Fulcrum webhooks.var fulcrumMiddleware = require('connect-fulcrum-webhook');// `request` allows you to make REST calls to APIS so that you can get, add, update or delete data. var request = require('request');// setting up the local port to listen onvar PORT = process.env.PORT || 9000;// setting up the express frameworkvar app = express();function payloadProcessor (payload, done) { if (payload.data.form_id && payload.data.form_id === "{FULCRUM FORM ID}"){ if (payload.type === "record.create") { createRecord(payload, done); } else if (payload.type === "record.update") { updateRecord(payload, done); } else if (payload.type === "record.delete") { deleteRecord(payload, done); } }}

view rawblock_1.js hosted with ❤ by GitHub

FYI the brackets {} in these code blocks indicate places where you need to insert your organization’s IDs.

‍

The Payload

Before we review the functions that the payloads get pushed to, it is useful to understand the structure of the payload, or data that gets pulled and pushed. The Fulcrum payload is a list of field keys and values per record. The field keys are four alphanumeric digits that are assigned to each field in your form. The values are the answers in the form. To grab the values of you custom fields, you will need to go through the form_values object and then find the appropriate element key of the field:

{ id: "1371c81d-367b-45d3-9f7c-91da5de9518e", type: "record.delete", owner_id: "00053caf-4b6e-4c86-88b6-64695895dffe", data: { status: "Completed", version: 3, id: "7553fd44-78bb-41eb-a453-8b301ae5e52e", form_id: "295eda4a-7795-4881-9f62-085a930b356e", project_id: "Wetlands", created_at: "2013-09-21T19:20:16Z", updated_at: "2013-09-21T19:20:27Z", client_created_at: "2013-09-21T19:20:16Z", client_updated_at: "2013-09-21T19:20:27Z", created_by: "larry", created_by_id: "960247b1-aa51-4d80-bfa1-a1d0baf8d87e", updated_by: "dev Test", updated_by_id: "960247b1-aa51-4d80-bfa1-a1d0baf8d87e", assigned_to: null, assigned_to_id: null, form_values: { 94f8: "I25 and Garden of the Gods", 8902: "Yes", 78h9: "Dan;8" }, latitude: 38.8968237991091, longitude: -104.830228686333, altitude: null, speed: null, course: null, horizontal_accuracy: null, vertical_accuracy: null }}

view rawpayload.js hosted with ❤ by GitHub

If you ever need to test out what the payload looks like before writing a script, you can use https://requestb.in/ to view the schema of your record. RequestBin allows users to inspect and test requests made to the URL that it provides. To use this, visit the RequestBin site, click “Create A Request Bin”, grab the URL, and paste that URL in your Fulcrum webhook page.

If the payload structure confuses you, you can read more about getting started with webhooks here.

‍

Create Record

When records are created in the original Fulcrum form, the script will push the data to the createRecord() function. This function grabs the data added to the record and then posts that data to a new form with the same schema (assuming that the forms are exact duplicates, and share the same field keys).

function createRecord(payload, done) { payload.record = payload.data; payload.record.form_id = "{FULCRUM SECOND FORM ID}"; // if you have a field that does not have the same element key for both forms then you can associate the fields // with this line (I explain what this is for in more detail in the next block): payload.record.form_values['{FULCRUM ELEMENT KEY}'] = payload.record.form_values['{FULCRUM ELEMENT KEY 2}']; delete payload.data; delete payload.record.id; request({ method: 'POST', url: 'https://api.fulcrumapp.com/api/v2/records.json', json: payload.record, headers: { 'X-ApiToken': '{API TOKEN}' } }, function (err, httpResponse, body) { console.log('create', err, body); }); done();}

view rawblock_2.js hosted with ❤ by GitHub

‍

Update Record

The PUT and DELETE requests are a little bit more complicated than the POST. Fulcrum assigns unique IDs to each and every record. This assignment makes it impossible to match record IDs from one form to another because they have different IDs. Therefore, to edit and delete a record from one app to another, we will need to find the matching record id. We can use the query API to do that.

‍

Adding A Field To Each Form

The way to solve this dilemma is to add a RECORDID() calculation field in the original form that gets pushed into a text field, called my_record_id. Using the query API we can create a SQL statement that grabs the record with the id that matches the my_record_id text field: "SELECT _record_id AS fulcrum_id FROM \"Table Name\" WHERE my_record_id= '" + payload.record.form_values['FULCRUM ELEMENT KEY'] + "'";`

function updateRecord(payload, done) { payload.record = payload.data; payload.record.form_id = '{FULCRUM SECOND FORM ID}'; // the line below equates the RECORDID() calculation field from the original form to the text field in the 2nd form // so that we can use the query API to find the correct correct to update from the original form payload.record.form_values['{FULCRUM ELEMENT KEY}'] = payload.record.form_values['{FULCRUM ELEMENT KEY 2}']; delete payload.data; var query = encodeURIComponent("SELECT _record_id AS fulcrum_id FROM \"Table Name\" WHERE my_record_id= '" + payload.record.form_values['FULCRUM ELEMENT KEY'] + "'"); request ({ method: 'GET', url: 'https://api.fulcrumapp.com/api/v2/query/?format=json&q=' + query, headers: { 'X-ApiToken': '{API TOKEN}', 'User-Agent': 'request' } }, function (err, httpResponse, body) { console.log(httpResponse, body); body = JSON.parse(body); console.log(body['rows'][0]['fulcrum_id']); if (body['rows'][0] && body['rows'][0]['fulcrum_id']){ request({ method: 'PUT', url: 'https://api.fulcrumapp.com/api/v2/records/' + body['rows'][0]['fulcrum_id'] + '.json', json: payload.record, headers: { 'X-ApiToken': '{API TOKEN}' } }, function (err, httpResponse, body) { console.log('PUT', err) console.log('PUT', body); }); }; done(); }); delete payload.record.id;}

view rawblock-3.js hosted with ❤ by GitHub

‍

Delete Record

This code works similarly to the PUT request. We use the query API to grab the proper record through its original ID. Then we delete the record that has the matching record ID.

function deleteRecord(payload, done) { payload.record = payload.data; payload.record.form_id = "{FULCRUM SECOND FORM ID}"; // this sets up the record id from the original form to the text field in the 2nd form payload.record.form_values['{FULCRUM ELEMENT KEY}'] = payload.record.form_values['{FULCRUM ELEMENT KEY 2}']; delete payload.data; var query = encodeURIComponent("SELECT _record_id AS fulcrum_id FROM \"Table Name\" WHERE my_record_id= '" + payload.record.form_values['FULCRUM ELEMENT KEY'] + "'"); request({ method: 'GET', url: 'https://api.fulcrumapp.com/api/v2/query/?format=json&q=' + query, headers: { 'X-ApiToken': '{API TOKEN}', 'User-Agent': 'request' } }, function (err, httpResponse, body) { console.log(err, body); console.log('before', body); body = JSON.parse(body); console.log('after', body); if (body['rows'][0] && body['rows'][0]['fulcrum_id']){ request({ method: 'DELETE', url: 'https://api.fulcrumapp.com/api/v2/records/' + body['rows'][0]['fulcrum_id'] + '.json', json: payload.record, headers: { 'X-ApiToken': '{API TOKEN}' } }, function (err, httpResponse, body) { console.log('DELETE', body); }); }; done(); });}

view rawblock-4.js hosted with ❤ by GitHub

‍

Bringing it All Together

The last few lines bring all of the functions together to seamlessly pull in the payload data from Fulcrum:

var fulcrumMiddlewareConfig = { actions: ['record.create', 'record.update', 'record.delete'], processor: payloadProcessor};app.use('/', fulcrumMiddleware(fulcrumMiddlewareConfig));// this is for decorative purposes. It adds a little HTML to the front end of your URL so that you can check to make sure things are up and runningapp.get('/', function (req, res) { res.send('<html><head><title>The Webhook Script</title></head><body><h2>is Running!</h2><p>going</p></body></html>');})// this function allows you to serve the script locally for testingapp.listen(PORT, function () { console.log('Listening on port ' + PORT);});

view rawapp.js hosted with ❤ by GitHub

You will need to host this script on a server, such as Heroku or AWS. When the script is online, you will need to place the URL of the script on your organization’s webhook link.

Now, you are able to sync records from one form to the other. That wasn’t so spooky, was it?

If you have not taken a stroll through our guides on webhooks, then I highly suggest you start today! Happy haunting!