The DevXP engineering team hosts office hours every Thursday at 11 a.m. Pacific Time where we answer your questions live and help you get up and running with Flatfile. Join us!

Inside your listener function, you can respond to any Event that occurs in the environment you’re listening to.

Your transformation and validation code is already responding to Record level Events, but a listener can respond to any Event, like the Action triggered by a button click.

Actions are defined in your Workbook Blueprint and backed by Jobs in your listener.

In this step of the tutorial, we’re going to configure our Workbook’s primary Action, defined in our Blueprint as the submitAction.

Adding behavior to the action

The primary Action button is located in the upper right corner of your Workbook.

When this button is clicked by a user, a Job is created. We’re going to listen for that job:ready event, and call out to a waiting webhook to export our data.

Import addl packages

You’ll need a few more packages to accomplish this task. Go ahead and import them.

import api from "@flatfile/api";
import { responseRejectionHandler } from "@flatfile/util-response-rejection";

Call a webhook

To configure our listener to call to a webhook, let’s create a webhook. We recommend using Simply navigate to their site and copy Your unique URL.

Now, update the value of the WEBHOOK_SITE_URL variable in your .env file with the webhook url you just copied.

  { job: "workbook:submitAction" },
  async (event: FlatfileEvent) => {
    const { jobId, workbookId } = event.context;
    const { data: workbook } = await api.workbooks.get(workbookId);
    const { data: workbookSheets } = await api.sheets.list({ workbookId });

    const sheets = [];
    for (const [_, element] of workbookSheets.entries()) {
      const { data: records } = await api.records.get(;

    try {
      await, {
        info: "Starting job to submit action to",
        progress: 10,

      // TODO: place your webhook url here
      const webhookReceiver = "";

      const response = await fetch(webhookReceiver, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        body: JSON.stringify({
          workbook: {

      if (response.status === 200) {
        const responseData = await response.json();
        const rejections = responseData.rejections;

        if (rejections) {
          const outcome = await responseRejectionHandler(rejections);
          await, outcome);
        await, {
          outcome: {
            message: `Data was successfully submitted to Go check it out at ${webhookReceiver}.`,
      } else {
        throw new Error("Failed to submit data to");
    } catch (error) {
      await, {
        outcome: {
            "This job failed probably because it couldn't find the URL.",
// See full code example (

Watch your Action

Check your listener is still running. If not, run npx flatfile@latest develop.

Now, in your Workbook click the “Submit” button in the top right corner.

Over on your browser tab look at the query log and observe that the webhook received a call; this is the Action running.

Next steps

Congratulations! You now now have an example listener that is performing transformations and validations in response to changes in your Workbook, and you’ve set up an action to export your cleaned data.

You’ve been running this listener locally while in development. Next, let’s see how easy it is to Deploy to Flatfile.