Back to Activepieces

Output Schema

docs/build-pieces/piece-reference/output-schema.mdx

0.85.44.1 KB
Original Source

By default, the builder renders a step's output as raw JSON. You can opt in to a friendly, labelled presentation by declaring an outputSchema on your action or trigger. The schema drives both the Smart Output Viewer (test step / run details) and the Data Selector (variable picker), giving users readable labels, formatted values, and nested/tabular views — without changing the expression paths used in automations.

The schema is fully type-checked against the OutputSchema type exported from @activepieces/pieces-framework.

Declare a Schema

Pass outputSchema with a flat list of fields. Each field maps to a path in the output:

typescript
import { createAction } from '@activepieces/pieces-framework';

export const getEmail = createAction({
  name: 'get_email',
  displayName: 'Get email',
  props: {},
  outputSchema: {
    fields: [
      { key: 'subject', label: 'Subject' },
      { key: 'from', label: 'From', format: 'email' },
      { key: 'date', label: 'Received', format: 'datetime' },
      {
        key: 'attachments',
        label: 'Attachments',
        listItems: [
          { key: 'fileName' },
          { key: 'size', format: 'filesize' },
        ],
      },
    ],
  },
  run: async (context) => {
    /* ... */
  },
});

A field supports an optional label, a value path override, a format (email, url, date, datetime, number, boolean, image, html, currency, filesize, duration), and children / listItems for nested objects and arrays of records.

Friendly Labels for Opaque Keys

When your output is an object keyed by UUIDs/slugs (dynamicKey: true) or an array of records (listItems), the entries would otherwise show as raw keys or Item 1, Item 2, … Use labelKey to point at a property inside each entry/item whose value should be shown as the label instead.

<Tip> The expression path is never affected — `labelKey` only changes what the user sees. Inserting a variable still references the opaque key (`step_1['<uuid>']`) or numeric index. This is ideal when keying by UUID: the key stays stable even if the name changes in the external system, so existing automations never break. </Tip>

Object keyed by ID:

typescript
outputSchema: {
  fields: [
    {
      key: 'boards',
      label: 'Boards',
      dynamicKey: true,
      labelKey: 'name',
    },
  ],
},
// run() returns: { boards: { '3f2504e0-...': { name: 'Roadmap', ... } } }

The data selector and output viewer show Roadmap instead of the UUID. The inserted expression is still step_1['boards']['3f2504e0-...'].

Array of records:

typescript
outputSchema: {
  fields: [
    {
      key: 'items',
      label: 'Items',
      labelKey: 'name',
      listItems: [{ key: 'id' }, { key: 'name' }],
    },
  ],
},

Each element is labelled by its name instead of Item 1, Item 2. When an item has no name (or it's empty), the label falls back to Item N.

<Note> `labelKey` is a path relative to each entry/item, so nested label sources work too (`labelKey: 'profile.fullName'`). It applies to the expanded (field-list) view; the tabular/column view derives its headers from property keys. </Note>

Top-Level Array Output

When your action returns a top-level array (e.g. a list of search results), the schema's fields describe a single item, and they are applied to every element of the array. Use the optional itemLabel template to label each item — {dotPath} placeholders are resolved against that item:

typescript
outputSchema: {
  itemLabel: '{key}: {fields.summary}',
  fields: [
    { key: 'key', label: 'Key' },
    { key: 'summary', label: 'Summary', value: 'fields.summary' },
    { key: 'status', label: 'Status', value: 'fields.status.name' },
  ],
},
// run() returns: [ { key: 'ADS-69', fields: { summary: 'Booking issue', status: { name: 'To Do' } } }, ... ]

Each item is shown as ADS-69: Booking issue and expands to the labelled fields. When the template resolves to an empty string, the label falls back to Item N. The inserted expression for an item is still its index (step_1[0]), and each field resolves under it (step_1[0]["fields"]["summary"]).