Skip to main content

Post-processing actions

Tripetto supports the use of headless action blocks. These blocks can be added to a form like normal question blocks. The difference is that an action block does not ask for a certain input from the respondent using visual input elements. Instead, it acts in the background. For example, the calculator block that can perform realtime calculations in the form.

Some action blocks generate data that needs post-processing after completion of a form. A good example of such an action block is the mailer block. It is used to compose an email message within a form. However, this message still needs to be sent when the form completes. This is where post-processing actions come into play.


In most setups, the post-processing needs to be done at the (server) endpoint that processes/receives your form data. That's because the runner is a purely client-side component.

🎬 Obtaining post-processing data

To make it easy to retrieve the data you need for post-processing, there is a special export function called actionables. It retrieves all fields from a form necessary for post-processing. So, for example, when your form includes a mailer block, the function retrieves the data fields necessary for composing and sending the email message.

A best practice is to submit the action data together with your exportable data to your data collection endpoint and then process it there. The following example shows how to submit both data structures to an endpoint.

import { run } from "@tripetto/runner-autoscroll";
import { Export } from "@tripetto/runner";

definition: /* Supply your form definition here */,
onSubmit: (instance) => new Promise((resolve, reject) => {
fetch("/example-server", {
method: "POST",
headers: {
"Content-Type": "application/json"
body: JSON.stringify({
// Post both the exportable and actionable data to the endpoint
exportables: Export.exportables(instance),
actionables: Export.actionables(instance)
.then((response) => {
if (response.ok) {
// All good, resolve the promise
} else {
// Not so good, reject the promise
.catch((error) => {
// Error occurred, reject with error message

🍳 Post-processing the data

After the endpoint receives the data, it needs to post-process it. In general, there is no need to store the actionable data. It is volatile data only necessary to perform the actions. We recommend performing validation and storage of the exportable data first. If that went well, process the actionable data and perform the actions.

The code for the post-processing of the action blocks depends on the technology stack used for your endpoint. Please have a look at the implementation in the Tripetto Studio (TypeScript, Node.js) or the Tripetto WordPress plugin (PHP) for a complete example:

Both implementations use the same approach. They iterate through the actionable data looking for the block identifier of the action blocks that need post-processing. Then the data required for the action is retrieved, and after some validation, the action executes.

// Retrieve the actionables object from the POST data
const actionables = req.body.actionables;

// Iterate through the nodes
if (actionables) {
actionables.nodes.forEach((node) => {
// Retrieve the type of node
switch (node.type) {
// Process the mailer block
case "@tripetto/block-mailer":
// Find the recipient
const recipient = => data.slot === "recipient")?.string || "";
// Find the subject
const subject = => data.slot === "subject")?.string || "";
// Find the message.
const message = => data.slot === "message")?.string || "-";
// Find the sender.
const sender = => data.slot === "sender")?.string || "";

// Do some validation
if (recipient && subject) {
// Send the email message here

🧱 Blocks that need post-processing

The following blocks used in the stock runners need post-processing: