# Flow Operations

> Operations are single steps in a Flow - the no-code automation tool in Directus.

Operations are single steps in a <product-link product="automate">



</product-link>

 - the no-code automation tool in Directus.

![An operation being created in a "New Subscription" flow](/img/d8598efa-ed8f-4d5b-924d-8e756b34b190.webp)

<partial content="extensions-api">



</partial>

Operations have two entrypoints - one for the Data Studio, and one for the server process when the flow is run.

## App Entrypoint

The `app.js` or `app.ts` file contains the configuration for the appearance and user-provided options of the operation.

### Entrypoint Example

```js
export default {
    id: 'custom',
    name: 'Custom',
    icon: 'box',
    description: 'This is my custom operation!',
    overview: ({ text }) => [
        {
            label: 'Text',
            text: text,
        },
    ],
    options: [
        {
            field: 'text',
            name: 'Text',
            type: 'string',
            meta: {
                width: 'full',
                interface: 'input',
            },
        },
    ],
};
```

### Options

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        id
      </code>
    </td>
    
    <td>
      A unique identifier for this extension.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        name
      </code>
    </td>
    
    <td>
      The displayed name for this panel in the Data Studio.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        icon
      </code>
    </td>
    
    <td>
      An icon name from the <a href="https://fonts.google.com/icons" rel="nofollow">
        Google Material Icons set
      </a>
      
      . Supports filled and outlined variants.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        description
      </code>
    </td>
    
    <td>
      A description of this panel shown in the Data Studio. Maximum 80 characters.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        overview
      </code>
    </td>
    
    <td>
      An overview that will be shown on the operation's tile. Can be either a function that receives the options of the operation and returns an array of objects containing <code>
        label
      </code>
      
       and <code>
        text
      </code>
      
       or a dedicated Vue component.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        options
      </code>
    </td>
    
    <td>
      The options of your operation. Can be either an options object or a dedicated Vue component.
    </td>
  </tr>
</tbody>
</table>

<partial content="extensions-uid">



</partial>

## API Entrypoint

The `api.js` or `api.ts` file contains the logic for the operation. It runs a handler function with the data passed from the App Entrypoint options.

### Entrypoint Example

This example assumes there is an object with a name of `text` in the App Entrypoint options.

```js
export default {
    id: 'custom',
    handler: (options) => {
        console.log(options.text);
    },
};
```

<callout icon="i-lucide-info">

The `id` in both the app and the api entrypoint must be the same.

</callout>

### Handler Function

The handler function is called when the operation is executed. It must return a value to trigger the `resolve` anchor or throw with a value to trigger the `reject` anchor. The returned value will be added to the [data chain](/guides/flows/data-chain).

The handler function receives `options` and `context`. `options` contains the operation's option values, while `context` has the following properties:

<table>
<thead>
  <tr>
    <th>
      Property
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        services
      </code>
    </td>
    
    <td>
      All API internal services.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        database
      </code>
    </td>
    
    <td>
      Knex instance that is connected to the current database.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        getSchema
      </code>
    </td>
    
    <td>
      Async function that reads the full available schema for use in services.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        env
      </code>
    </td>
    
    <td>
      Parsed environment variables.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        logger
      </code>
    </td>
    
    <td>
      <a href="https://github.com/pinojs/pino" rel="nofollow">
        Pino
      </a>
      
       instance.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        data
      </code>
    </td>
    
    <td>
      Object containing the raw data returned by the previous operations.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        accountability
      </code>
    </td>
    
    <td>
      Information about the current user received by the trigger.
    </td>
  </tr>
</tbody>
</table>

## Sandboxed Operations

### TypeScript

You can import the `SandboxOperationConfig` type from `directus:api` to type the register function's `context` object:

```ts
/// <reference types="@directus/extensions/api.d.ts" />
import type { SandboxOperationConfig } from "directus:api";

const operation: SandboxOperationConfig = {
    id: 'custom',
    handler: (options) => {
    },
};

export default operation;
```

The `handler` function receives the `options` object of the current flow.

<callout icon="i-lucide-book-open" color="primary" to="/guides/extensions/api-extensions/sandbox">

Learn more about the Directus sandbox for API extensions.

</callout>

<partial content="extensions-api-internals">



</partial>
