> ## Documentation Index
> Fetch the complete documentation index at: https://flatfile.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 01: Creating Your First Listener

> Learn to set up a basic Listener with Space configuration to define your data structure and workspace layout.

<Note>
  If you aren't interested in a code-forward approach, we recommend starting with [AutoBuild](/getting-started/quickstart/autobuild.mdx), which uses AI to analyze your template or documentation and then automatically creates and deploys a [Blueprint](/core-concepts/blueprints) (for schema definition) and a [Listener](/core-concepts/listeners) (for validations and transformations) to your [Flatfile App](/core-concepts/apps).

  Once you've started with AutoBuild, you can always download your Listener code and continue building with code from there!
</Note>

## What We're Building

In this tutorial, we'll build a foundational Listener that handles Space configuration - the essential first step for any Flatfile implementation. Our Listener will:

* **Respond to Space creation**: When a user creates a new [Space](/core-concepts/spaces), our Listener will automatically configure it
* **Define the Blueprint**: Set up a single [Workbook](/core-concepts/workbooks) with a single [Sheet](/core-concepts/sheets) with [Field](/core-concepts/fields) definitions for names and emails that establishes the data schema for the Space
* **Handle the complete Job lifecycle**: Acknowledge, update progress, and complete the configuration [Job](/core-concepts/jobs) with proper error handling
* **Provide user feedback**: Give real-time updates during the configuration process

This forms the foundation that you'll build upon in the next parts of this series, where we'll add user Actions and data validation. By the end of this tutorial, you'll have a working Listener that creates a fully configured workspace ready for data import.

## Prerequisites

Before we start coding, you'll need a Flatfile account and a fresh project directory:

1. **Create a new project directory**: Start in a fresh directory for this tutorial (e.g., `mkdir my-flatfile-listener && cd my-flatfile-listener`)
2. **Sign up for Flatfile**: Visit [platform.flatfile.com](https://platform.flatfile.com) and create your free account
3. **Get your credentials**: You'll need your Secret Key and Environment ID from the [Keys & Secrets](https://platform.flatfile.com/dashboard/keys-and-secrets) section later in this tutorial

<Note>
  **New to Flatfile?** If you'd like to understand the broader data structure and concepts before diving into code, we recommend reading through the [Core Concepts](/core-concepts/overview) section first. This covers the foundational elements like [Environments](/core-concepts/environments), [Apps](/core-concepts/apps), and [Spaces](/core-concepts/spaces), as well as our data structure like [Workbooks](/core-concepts/workbooks) and [Sheets](/core-concepts/sheets), and how they all work together.

  Each Listener is deployed to a specific Environment, allowing you to set up separate Environments for development, staging, and production to safely test code changes before deploying to production.
</Note>

## Install Dependencies

Choose your preferred language and follow the setup steps:

<CodeGroup>
  ```bash JavaScript theme={null}
  # Initialize project (skip if you already have package.json)
  npm init -y

  # Install required Flatfile packages
  npm install @flatfile/listener @flatfile/api

  # Note: Feel free to use your preferred JavaScript project setup method instead
  ```

  ```bash TypeScript theme={null}
  # Initialize project (skip if you already have package.json)
  npm init -y

  # Install required Flatfile packages
  npm install @flatfile/listener @flatfile/api

  # Install TypeScript dev dependency
  npm install --save-dev typescript

  # Initialize TypeScript config (skip if you already have tsconfig.json)
  npx tsc --init

  # Note: Feel free to use your preferred TypeScript project setup method instead
  ```
</CodeGroup>

### Authentication Setup

For this step, you'll need to get your Secret Key and environment ID from your [Flatfile Dashboard](https://platform.flatfile.com/dashboard/keys-and-secrets).

Then create a new file called `.env` and add the following (populated with your own values):

```bash theme={null}
# .env
FLATFILE_API_KEY="your_secret_key"
FLATFILE_ENVIRONMENT_ID="us_env_your_environment_id"
```

## Create Your Listener File

Create a new file called `index.js` for Javascript or `index.ts` for TypeScript:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import api from "@flatfile/api";

  export default function (listener) {
    // Configure the Space when it's created
    listener.on("job:ready", { job: "space:configure" }, async (event) => {
      const { jobId, spaceId } = event.context;
      try {
        // Acknowledge the job
        await api.jobs.ack(jobId, {
          info: "Setting up your workspace...",
          progress: 10,
        });
        // Create the Workbook with Sheets, creating the Blueprint for the space
        await api.workbooks.create({
          spaceId,
          name: "My Workbook",
          sheets: [
            {
              name: "contacts",
              slug: "contacts",
              fields: [
                { key: "name", type: "string", label: "Full Name" },
                { key: "email", type: "string", label: "Email" },
              ],
            },
          ],
        });
        // Update progress
        await api.jobs.update(jobId, {
          info: "Workbook created successfully",
          progress: 75,
        });
        // Complete the job
        await api.jobs.complete(jobId, {
          outcome: {
            message: "Workspace configured successfully!",
            acknowledge: true,
          },
        });
      } catch (error) {
        console.error("Error configuring Space:", error);
        // Fail the job if something goes wrong
        await api.jobs.fail(jobId, {
          outcome: {
            message: `Failed to configure workspace: ${error.message}`,
            acknowledge: true,
          },
        });
      }
    });
  }

  ```

  ```typescript TypeScript theme={null}
  import type { FlatfileListener } from "@flatfile/listener";
  import api from "@flatfile/api";

  export default function (listener: FlatfileListener) {
    // Configure the Space when it's created
    listener.on("job:ready", { job: "space:configure" }, async (event) => {
      const { jobId, spaceId } = event.context;
      
      try {
        // Acknowledge the job
        await api.jobs.ack(jobId, {
          info: "Setting up your workspace...",
          progress: 10
        });

        // Create the Workbook with Sheets, creating the Blueprint for the space
        await api.workbooks.create({
          spaceId,
          name: "My Workbook",
          sheets: [
            {
              name: "contacts",
              slug: "contacts",
              fields: [
                { key: "name", type: "string", label: "Full Name" },
                { key: "email", type: "string", label: "Email" },
              ],
            },
          ],
        });

        // Update progress
        await api.jobs.update(jobId, {
          info: "Workbook created successfully",
          progress: 75
        });

        // Complete the job
        await api.jobs.complete(jobId, {
          outcome: {
            message: "Workspace configured successfully!",
            acknowledge: true
          }
        });

      } catch (error) {
        console.error("Error configuring Space:", error);
        
        // Fail the job if something goes wrong
        await api.jobs.fail(jobId, {
          outcome: {
            message: `Failed to configure workspace: ${error instanceof Error ? error.message : 'Unknown error'}`,
            acknowledge: true
          }
        });
      }
    });
  }
  ```
</CodeGroup>

<Note>
  **Complete Example**: The full working code for this tutorial step is available in our Getting Started repository: [JavaScript](https://github.com/FlatFilers/getting-started/tree/main/101.01-first-listener/javascript) | [TypeScript](https://github.com/FlatFilers/getting-started/tree/main/101.01-first-listener/typescript)
</Note>

## Project Structure

After creating your Listener file, your project directory should look like this:

<CodeGroup>
  ```text JavaScript theme={null}
  my-flatfile-listener/
  ├── .env      // Environment variables
  ├── index.js  // Listener code
  |
  |   /* Node-specific files below */
  |
  ├── package.json
  ├── package-lock.json
  └── node_modules/
  ```

  ```text TypeScript theme={null}
  my-flatfile-listener/
  ├── .env      // Environment variables
  ├── index.ts  // Listener code
  |
  |   /* Node and Typescript-specific files below */
  |
  ├── package.json
  ├── package-lock.json
  ├── tsconfig.json
  └── node_modules/
  ```
</CodeGroup>

### Authentication Setup

You'll need to get your Secret Key and Environment ID from your [Flatfile Dashboard](https://platform.flatfile.com/dashboard/keys-and-secrets) to find both values, then add them to a `.env` file:

```bash theme={null}
# .env
FLATFILE_API_KEY="your_secret_key"
FLATFILE_ENVIRONMENT_ID="us_env_your_environment_id"
```

## Testing Your Listener

### Local Development

To test your Listener locally, you can use the `flatfile develop` command. This will start a local server that implements your custom Listener code, and will also watch for changes to your code and automatically reload the server.

```bash theme={null}
# Run locally with file watching
npx flatfile develop
```

### Step-by-Step Testing

After running your listener locally:

<Card title="Testing Steps">
  1. Create a new space in your Flatfile environment
  2. Observe as the new space is configured with a Workbook and Sheet
</Card>

## What Just Happened?

Your Listener is now ready to respond to Space configuration Events! Here's how the space configuration works step by step:

### 1. Exporting your Listener function

This is the base structure of your Listener. At its core, it's just a function that takes a `listener` object as an argument, and then uses that listener to respond to Events.

<CodeGroup>
  ```javascript JavaScript theme={null}
  export default function (listener) {
    // . . . code
  }
  ```

  ```typescript TypeScript theme={null}
  export default function (listener: FlatfileListener) {
    // . . . code
  }
  ```
</CodeGroup>

### 2. Listen for Space Configuration

When a new Space is created, Flatfile automatically triggers a `space:configure` job that your Listener can handle. This code listens for that job using the `job:ready` Event, filtered by the job name `space:configure`.

<CodeGroup>
  ```javascript JavaScript theme={null}
  listener.on("job:ready", { job: "space:configure" }, async (event) => {
    // . . . code
  });
  ```

  ```typescript TypeScript theme={null}
  listener.on("job:ready", { job: "space:configure" }, async (event) => {
    // . . . code
  });
  ```
</CodeGroup>

### 3. Acknowledge the Job

The first step is always to acknowledge that you've received the job and provide initial feedback to users. From this point on, we're responsible for the rest of the job lifecycle, and we'll be doing it all in this Listener. For more information on Jobs, see the [Jobs](/core-concepts/jobs) concept.

<CodeGroup>
  ```javascript JavaScript theme={null}
  await api.jobs.ack(jobId, {
    info: "Setting up your workspace...",
    progress: 10,
  });
  ```

  ```typescript TypeScript theme={null}
  await api.jobs.ack(jobId, {
    info: "Setting up your workspace...",
    progress: 10
  });
  ```
</CodeGroup>

### 4. Define the Blueprint

Next, we create the workbook with sheets and field definitions. This **is** your [Blueprint](/core-concepts/blueprints) definition—establishing the data schema that will govern all data within this Space.

<CodeGroup>
  ```javascript JavaScript theme={null}
  await api.workbooks.create({
    spaceId,
    name: "My Workbook",
    sheets: [
      {
        name: "contacts",
        slug: "contacts", 
        fields: [
          { key: "name", type: "string", label: "Full Name" },
          { key: "email", type: "string", label: "Email" },
        ],
      },
    ],
  });
  ```

  ```typescript TypeScript theme={null}
  await api.workbooks.create({
    spaceId,
    name: "My Workbook",
    sheets: [
      {
        name: "contacts",
        slug: "contacts",
        fields: [
          { key: "name", type: "string", label: "Full Name" },
          { key: "email", type: "string", label: "Email" },
        ],
      },
    ],
  });
  ```
</CodeGroup>

### 5. Update Progress

Keep users informed about what's happening during the configuration process.

<CodeGroup>
  ```javascript JavaScript theme={null}
  await api.jobs.update(jobId, {
    info: "Workbook created successfully",
    progress: 75,
  });
  ```

  ```typescript TypeScript theme={null}
  await api.jobs.update(jobId, {
    info: "Workbook created successfully", 
    progress: 75
  });
  ```
</CodeGroup>

### 6. Complete the Job

Finally, mark the job as complete with a success message, or fail it if something went wrong.

<CodeGroup>
  ```javascript JavaScript theme={null}
  // Success case
  await api.jobs.complete(jobId, {
    outcome: {
      message: "Workspace configured successfully!",
      acknowledge: true,
    },
  });

  // Failure case
  await api.jobs.fail(jobId, {
    outcome: {
      message: `Failed to configure workspace: ${error.message}`,
      acknowledge: true,
    },
  });
  ```

  ```typescript TypeScript theme={null}
  // Success case
  await api.jobs.complete(jobId, {
    outcome: {
      message: "Workspace configured successfully!",
      acknowledge: true
    }
  });

  // Failure case
  await api.jobs.fail(jobId, {
    outcome: {
      message: `Failed to configure workspace: ${error instanceof Error ? error.message : 'Unknown error'}`,
      acknowledge: true
    }
  });
  ```
</CodeGroup>

This follows the standard Job pattern: **acknowledge → update progress → complete** (or fail on error). This provides users with real-time feedback and ensures robust error handling throughout the configuration process.

## Next Steps

Ready to enhance data quality? Continue to [Adding Validation](/coding-tutorial/101-your-first-listener/101.02-adding-validation) to learn how to validate Fields and provide real-time feedback to users.

For more detailed information:

* Understand Job lifecycle patterns in [Jobs](/core-concepts/jobs) and [Spaces](/core-concepts/spaces)
* Learn more about [Events](/reference/events)
* Organize your Listeners with [Namespaces](/guides/namespaces-and-filters)
