Using Fillout Forms to build multiple related records

I had been using tally for my forms and then the other week I spotted that Fillout had added Subforms which looked like an interesting feature they use the example of adding multiple family members, it could also be multiple attendees to an event and I built something around multiple rooms.

This video shows it in action:

IMPORTANT
There are easier ways to build a quote system the point of this is to show how you can integrate Tape with powerful form systems and how you can use JSONata to extract information from complex and changing Webhook Payloads.

There are several automations (flows) involved in the video above however the most important is what handles the incoming form data, this comes into tape via a webhook and looks like this:

{
  "fields": [
    {
      "id": "svKPqNARf13fdqHpKiesEV",
      "label": "Add a room",
      "type": "Subform",
      "value": [
        {
          "___fillout_submission_id": "4f455da2-76fa-4111-89fd-62553506e10e",
          "urlParams": {},
          "stepHistory": {
            "path": [
              "2xmZ2PfYc9"
            ]
          },
          "calculations": {
            "bAXi4MdkkdGYb4pbhht8kK": 1
          },
          "globals": {
            "submissionId": "4f455da2-76fa-4111-89fd-62553506e10e"
          },
          "quiz": {},
          "2xmZ2PfYc9": {
            "dqoKwYStbYfTupTxSzyw1Q": {
              "value": 2
            },
            "gRWVBDL7dgcbrPU9XS8Bgu": {
              "value": "Kitchen",
              "selectedOptionIds": [
                "gg5Ro686BPbvxivFMnjKqh"
              ]
            },
            "hVfjUKF5RVnNcbw9SeptHe": {
              "value": 1
            },
            "iYnpD9rtWvkaHhPSyBuF3k": {
              "value": "Garden Kitchen"
            },
            "kjmLQCeaHx6cmnVMoFrB72": {
              "value": 1
            },
            "rZhB24itXEENdzkDTt57KZ": {
              "value": 1
            },
            "sNXJDMwRdWRKnqgR6S9NbT": {
              "value": 3
            },
            "sXegXVFutvRykWythudfHV": {
              "value": 4
            }
          },
          "ojq4R9hWhV": {},
          "___fillout_submission_status": "finished"
        },
        {
          "___fillout_submission_id": "0149e9dd-d7d7-4f5a-9fc2-958b4fc00189",
          "urlParams": {},
          "stepHistory": {
            "path": [
              "2xmZ2PfYc9"
            ]
          },
          "calculations": {
            "bAXi4MdkkdGYb4pbhht8kK": 1
          },
          "globals": {
            "submissionId": "0149e9dd-d7d7-4f5a-9fc2-958b4fc00189"
          },
          "quiz": {},
          "2xmZ2PfYc9": {
            "dqoKwYStbYfTupTxSzyw1Q": {
              "value": 2
            },
            "gRWVBDL7dgcbrPU9XS8Bgu": {
              "value": "Bedroom",
              "selectedOptionIds": [
                "daZWixdEVHsSviDaKxsoCY"
              ]
            },
            "hVfjUKF5RVnNcbw9SeptHe": {
              "value": 2
            },
            "iYnpD9rtWvkaHhPSyBuF3k": {
              "value": "Master Bedroom"
            },
            "kjmLQCeaHx6cmnVMoFrB72": {
              "value": 1
            },
            "rZhB24itXEENdzkDTt57KZ": {
              "value": null
            },
            "sNXJDMwRdWRKnqgR6S9NbT": {
              "value": 3
            },
            "sXegXVFutvRykWythudfHV": {
              "value": 4
            }
          },
          "ojq4R9hWhV": {},
          "___fillout_submission_status": "finished"
        }
      ]
    },
    {
      "id": "xApw5wNkWSjYPiqAe2WLp8",
      "label": "Total Number of rooms",
      "type": "NumberInput",
      "value": 1
    },
    {
      "id": "ref",
      "label": "ref",
      "type": "url_param",
      "value": "45368987"
    },
    {
      "id": "client",
      "label": "client",
      "type": "url_param",
      "value": "Jason"
    }
  ],
  "submission_id": "f658562e-9c00-463f-97bb-f600cea8afe3",
  "submission_time": "2024-02-29T16:20:20.539Z",
  "form_id": "55enV3dodFus",
  "form_name": "LAR Test"
}

You can have any number of rooms (subforms) with different items in each so the payload can be a little complex.

First off we need to grab some information to enable further actions:

const rooms = jsonata('fields.value.*["2xmZ2PfYc9"].gRWVBDL7dgcbrPU9XS8Bgu.value').evaluate(webhook_payload_parsed);
// Build a list of room ID's to loop through
const rid = jsonata('fields.value.___fillout_submission_id').evaluate(webhook_payload_parsed);
console.info('Rooms', JSON.stringify(rooms));
console.info('Room IDs', JSON.stringify(rid));
// Get a unique number for the form
var_formid = jsonata('fields.value[0].globals.submissionId').evaluate(webhook_payload_parsed);
var_rooms = rooms;
var_roomid = rid
[18:50:21.864] Rooms, ["Kitchen","Bedroom"]
[18:50:21.865] Room IDs, ["4f455da2-76fa-4111-89fd-62553506e10e","0149e9dd-d7d7-4f5a-9fc2-958b4fc00189"]

So we end up with 3 variables and list of room types, a list of room ID’s (we will use this to loop through) and the overall form ID.

As the form could be started directly rather than via a link generated in Tape we need to check if that is the case and we do that by looking for the record ID in the Payload if it doesn’t exist we create it and add the form ID we got in the last step if it does exist we update the record again adding the form ID:

By doing this, we have a record with a unique identifier we know that we can now search on so we clear our collection and search again only this time for the form id which will give us the record we want to work with.

Note:
The form id also triggers the removal of the form link button.

The whole process can take a while depending on how many rooms and how many items per room so we post a comment saying that the process has started.

We can now loop through all of our rooms using the var_roomid we made earlier:

We need to start extracting the data for the room into variables we can use to build out the room:

var_room = jsonata(`fields.value[$.___fillout_submission_id = "${var_roomid}"]."2xmZ2PfYc9".gRWVBDL7dgcbrPU9XS8Bgu.value`).evaluate(webhook_payload_parsed);
// console.info('room:' var_room);
var_roomname = jsonata(`fields.value[$.___fillout_submission_id = "${var_roomid}"]."2xmZ2PfYc9".iYnpD9rtWvkaHhPSyBuF3k.value`).evaluate(webhook_payload_parsed);
var_data = jsonata(`fields.value[$.___fillout_submission_id = "${var_roomid}"]."2xmZ2PfYc9".hVfjUKF5RVnNcbw9SeptHe.value`).evaluate(webhook_payload_parsed);
var_lightswitch = jsonata(`fields.value[$.___fillout_submission_id = "${var_roomid}"]."2xmZ2PfYc9".kjmLQCeaHx6cmnVMoFrB72.value`).evaluate(webhook_payload_parsed);
var_downlighter = jsonata(`fields.value[$.___fillout_submission_id = "${var_roomid}"]."2xmZ2PfYc9".sNXJDMwRdWRKnqgR6S9NbT.value`).evaluate(webhook_payload_parsed);
var_socket = jsonata(`fields.value[$.___fillout_submission_id = "${var_roomid}"]."2xmZ2PfYc9".sXegXVFutvRykWythudfHV.value`).evaluate(webhook_payload_parsed);
var_oven = jsonata(`fields.value[$.___fillout_submission_id = "${var_roomid}"]."2xmZ2PfYc9".rZhB24itXEENdzkDTt57KZ.value`).evaluate(webhook_payload_parsed);
var_roomnum = jsonata(`fields.value[$.___fillout_submission_id = "${var_roomid}"]."2xmZ2PfYc9".dqoKwYStbYfTupTxSzyw1Q.value`).evaluate(webhook_payload_parsed);

We can do that by using jsonata to evaluate the payload and adding the relevant room ID to each evaluation string.

once we have our information we can create our room record, I do another conditional check here which is fairly irrelevant as I added the unique identifier search on the form ID however I left it in as it shows an alternative way:

What we are doing is creating the room record and linking it to the ‘quote’ record.

In the next part of the loop we go through each item check if that room has any and create a related record back to the room record with the quantity:

The final part is a little housekeeping and a comment to say all is finished:

The ‘Quote’ record also uses a few of Tape’s powerful calculation fields.

The most obvious is the form link button that only appears in certain situations:

// Build the personalised form link:
// the base url
const b = 'https://forms.fillout.com/t/55enV3dodFus?';
// the client name from the related record
const c = @All of Name[0].trim();
// the Record ID
const r = @ref;
// put it all together
const fl = `${b}client=${c}&ref=${r}`;
// create an empty variable
let fURL = "";

// if there is no form ID and there is a Record ID in the Ref field then fill the empty fURL with the button
if (!@formRef && @ref){
  fURL = `
<style>
  button {
    margin: 4px 4px;
	background-color: #1F365C;
    color: #FFCE00;
	padding: 15px 30px;
	font-size: 18px;
  }
</style>
<a target="_blank" href="${fl}"><button>Click Here to Open Form</button></a>`;
}

// Display the fURL variable
fURL;
3 Likes

Jason, great work as always! :100:

1 Like