# Getting Started

Feathery helps teams automate data intake workflows with detailed UI, powerful logic, and bidirectional integrations - without needing engineering resources.

New to Feathery? Follow the guide below to quickly launch your first workflow in Feathery.

{% embed url="<https://app.guideflow.com/embed/qkqvwmh4px>" %}

### Creating a basic workflow

#### 1. Create a workflow

After signing up for your [Feathery](https://www.feathery.io/) account, you can pick a template to use for your first workflow.&#x20;

![](/files/fkMrOL4dUPkRB9250a1g)

{% hint style="info" %}
Check out the **Sample Guide** form in your new organization to learn your way around.
{% endhint %}

#### 2. Customize your workflow

Next, you'll be redirected into the workflow designer. You can add form elements from the left side panel, and by clicking on the elements in your workflow, you'll be able to edit their properties and styles on the right side panel. You can also update your form layout by adding, removing, and splitting cells.

![](/files/SVoUZTtbVQ056jfIJOM9)

#### 3. Publish

The editor autosaves your workflow for you as a working draft. You can make changes to it over multiple sessions until you're ready to publish by pressing the `Publish` button in the upper right of your screen. To share, click `Live Form`, which will open a new tab with a unique URL for your form that you can copy and paste.

![](/files/Y4brjoBKWwlUYPYItqAJ)


# Form Fields

Fields are the fundamental unit of data in Feathery. Learn how they behave and how to use them.

Fields are the fundamental unit of data in Feathery. A field can be used to collect a piece of information from one of your end users.

Field IDs must be unique across all fields and across forms. Make sure to give your field a readable name so you can easily identify it. The max length of a form or hidden field ID is 128 characters.

## Form vs Hidden Field

Feathery supports two types of fields, **form fields** and **hidden fields**.

### [Form Fields](/platform/build-forms/elements/fields)

Form fields are the fields visible to your end users who fill them out. These are added to your form from the visual editor as [text fields](/platform/build-forms/elements/fields/text-field), [dropdowns](/platform/build-forms/elements/fields/dropdown), [file uploads](/platform/build-forms/elements/fields/file-upload), and more.

### [Hidden Fields](/platform/form-fields/hidden-fields)

Hidden fields are fields that your end users don't directly fill out, but they can be used to control the behavior of your form and run custom logic. [Learn more](/platform/form-fields/hidden-fields)

## Resources

* Different ways to set a field value
* [Link a field across multiple forms](/platform/form-fields/link-fields-across-forms)

They can be populated via an integration like [Plaid](https://www.feathery.io/integrations/plaid), set via our [API](https://api-docs.feathery.io/#create-field-value-for-user) or [embed SDK](/develop/react), and even passed in through Feathery-hosted form URLS. The structure of field values set from our hosted form URLs looks like `form.feathery.io/to/<form slug>/?<field-id>=<field-value>`. To specify multiple values for a given field, you can specify the query parameter in the URL multiple times.


# Hidden Fields

Hidden fields are fields that aren't visible to your end users and can't be directly filled out from the form.

Hidden fields are fields that aren't visible to your end users and can't be directly filled out from the form. However, you can use them to control the behavior of your form and run custom logic.

## Example

You might want to pre-populate a hidden field that sets the industry of your end user accessing the form. Based on the industry, you create [conditional logic](/platform/build-forms/logic/navigate-steps-conditionally) in your form that takes them to different questions that are industry specific.

## Creating Hidden Fields

To create a hidden field, go to your form settings and click the `Data Tracking`tab. Then, select `Edit Hidden Fields` button to access the field selector interface. Click `Create New` to create a new hidden field that you can use.

<figure><img src="/files/k4NCO1P5DFc0mBSTSdSZ" alt=""><figcaption></figcaption></figure>

They can be populated via an integration like [Plaid](https://www.feathery.io/integrations/plaid), set via our [API](https://api-docs.feathery.io/#create-field-value-for-user) or [embed SDK](/develop/react), and even passed in through Feathery-hosted form URLS. The structure of field values set from our hosted form URLs looks like `form.feathery.io/to/<form slug>/?<field-id>=<field-value>`. To specify multiple values for a given field, you can specify the query parameter in the URL multiple times.

Hidden fields starting with the text "feathery." are reserved.  You will not be able to create a field named this way nor modify one of the existing fields named this way.

## View Hidden Field Results

Your hidden field results can be viewed the same way as your form field results, within the `Results` tab of the Feathery dashboard.

<figure><img src="/files/ZYVAOv0tG3PZjHS0Bcju" alt=""><figcaption></figcaption></figure>


# How to Set Field Values

There are many ways you can set a Feathery form or hidden field value.

Aside from the user manually entering values for form fields, there are a variety of ways you can automatically set the values for both form and hidden fields.

## Default Value

You can set the default value of your field by selecting it in the `Designer`, going to the `Properties` tab, and setting a value under `Default Value`.

## URL Parameters

If you chose to send your users to your Feathery-hosted form rather than embed your form, you can pre-populate fields in your form by adding the values to your URL. The structure of field values set from our hosted form URLs looks like `form.feathery.io/to/<form slug>/?<field-id>=<field-value>`. To specify multiple values for a given field, you can specify the query parameter in the URL multiple times.

## Custom Logic

Using Feathery's [custom logic](/platform/build-forms/advanced-logic) feature, you can update field values without writing code or with basic Javascript.

## Embed Library

If you've embedded your form via Feathery's [embed library](/develop/react), you can use [a function](/develop/react/api-guide/setfieldvalues) to easily set field values for that user.

## Public API

You can send requests to Feathery's [public API](https://api-docs.feathery.io/#create-field-value-for-user) to update field values for any of your users.

## Third-Party Integrations

A number of our data enrichment integrations, such as [Plaid](https://www.feathery.io/integrations/plaid), will automatically populate the fields that you map to their data properties when the user activates them.


# Link Fields Across Forms

Your fields can be linked together to share data and properties.

Each Feathery field is available across all forms and integrations. If you want to use a field across multiple forms, look for the same field ID. The ID of a field doesn't change across forms.

## Form Fields

Any field you add to a form can be linked to any other form field, whether it's on the current form or a different form.

If two fields are linked, they share the same data but can be styled separately. This means if one of your linked fields is filled out, the data will show up for your user on the other linked field too.

Linked form fields also share the same data properties, such as max length, allowed characters, etc.

<figure><img src="/files/ufzi5QwcbWvlbGqCPqQ0" alt=""><figcaption></figcaption></figure>

## Hidden Fields

Hidden fields are by default available across all of your forms. Just look for the relevant field when choosing your hidden field to use.

<figure><img src="/files/dO9kkQBZUTGdO80X8Jdl" alt=""><figcaption></figcaption></figure>


# Custom Field Input Format

To customize the allowed input for a field, you can use the [Text Field](/platform/build-forms/elements/fields/text-field), which allows you to configure properties like max and min length, allowed characters, custom prefix & suffix, and more.


# Build Forms

A detailed guide for navigating the Feathery dashboard while building a form.

## Design

The Design tab is where you can:

* Create responsive page layouts
* Add and style your form elements
* Create new steps
* Set up conditional show/hide logic & navigation rules
* Preview & publish your form

{% hint style="info" %}
Easily **`Undo`** and **`Redo`** changes with the buttons in the top right corner next to **`Publish`**
{% endhint %}

#### How do form layouts work?

For simple forms, just drag elements in directly below and above each other.

For more complex layouts, use containers to group elements together and edit the container styles to control alignment, gaps, margin, & padding.

<figure><img src="/files/QS1gLtaZwcJJ1YWEZpbt" alt=""><figcaption></figcaption></figure>

Containers can also be nested in other containers to create hierarchical structures for more complex layouts.

Two designers can simultaneously edit different steps of the form, but they cannot edit the same step at the same time.

{% content-ref url="/pages/QZEbicM9sc0naFrf4ER6" %}
[Design](/platform/build-forms/design)
{% endcontent-ref %}

## Flow

In the Flow tab, you can:

* Add and edit step connections
* Customize your form navigation rules
* Add conditional logic for navigation

{% hint style="info" %}
Create a new connection by dragging from the + on the step. This will create an arrow to the step you need to connect.
{% endhint %}

<figure><img src="/files/yh5IGjEoqTFC8wvNnAwE" alt=""><figcaption></figcaption></figure>

## Logic

The `Logic` tab is where you can set up advanced behavior for your form, including calculations, custom Javascript, API connections, and more.

Two designers can simultaneously edit different logic rules, but they cannot edit the same logic rule at the same time.

{% content-ref url="/pages/lsLvzQNLSA1wtncQ3TFn" %}
[Logic](/platform/build-forms/logic)
{% endcontent-ref %}

## Results

The Results tab is where you can:

* View and manage all form submissions
* Review individual user submissions
* Download submissions as a CSV file
* Upload submissions from a CSV file
* Locate user IDs

<figure><img src="/files/2JMiTAMmYrg9Qn6CGbI1" alt=""><figcaption></figcaption></figure>

#### Upload Submissions From CSV File

Feathery supports the ability to bulk upload submission data from a CSV file:&#x20;

<figure><img src="/files/4EUA4FWTnD6JzT4lzsg6" alt=""><figcaption></figcaption></figure>

* In your CSV file, the first row must contain a 'User I&#x44;**'** (case-insensitive) column along with Feathery field IDs from your form. Each subsequent row represents a submission entry.
* If an existing User ID is specified, the existing submission for that user will be updated.

<figure><img src="/files/9Qlt1m5gdKvGvqXhcAdZ" alt=""><figcaption></figcaption></figure>

## Settings

The Settings tab is where you can:

* Edit the form name
* Switch your form theme
* Edit the URL the form redirects to upon completion
* Find the form ID, used to embed your form
* Configure how form validation errors are displayed to the user
  * [Built In](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#using_built-in_form_validation): browser-native, non-stylable error messages that float on the form
  * Inline: Feathery-styled error messages that appear within the form flow
* Toggle general user tracking
  * If on, users will automatically be tracked from one session to another, including across different forms. All of their submitted data will appear as one submission. If off, each session will count as a separate submission.
  * If user tracking is on, you can optionally configure location tracking
    * If on, users will automatically start from the step they left off when they leave the form and come back.
  * If user tracking is on, you can optionally configure data tracking
    * If on, users will automatically see their previously filled-in form data when they leave the form and come back.
  * **Note that the user tracking config applies across all forms but location and data tracking can be configured per-form.**
* Change the theme associated with the form
* And much more.

![](/files/mb5QIuTUqJact9FBSrGz)


# Elements

A detailed description of the elements available to you as you're building a Feathery form.

**Elements** are the components that make up your form.  They include:

* Basic Elements
  * These components do not collect data but have other uses such as improving its visual presentation, triggering actions like navigation, or showing form progression.
* Fields
  * Something on your form that the user can interact with to input data.

They can be dragged and dropped in from the left selection panel.

![](/files/sBUSJg9Laz5JOqMCSpF2)


# Basic

Basic Feathery elements include all non-field elements, including buttons, text, progress bars, images, and more.

Design your form with customizable components.

<figure><img src="/files/3WIarT6GNhSwcaI7W2Fv" alt="Screenshot of button element selected."><figcaption></figcaption></figure>

### How to Use Basic Elements

Drag your element to an empty container to customize it.

#### **Styles**

Modify the element padding, scale, typography, and color styles.

{% hint style="info" %}
Scroll to the bottom of the style menu to set Hover and Selected styles.
{% endhint %}

#### **Properties**

Customize element behaviors like click actions and more.

![](/files/rfpGSJ2cPW9lrUyJ3X6u)

{% content-ref url="/pages/Jct3fFLmosqHESFY0SRl" %}
[Button](/platform/build-forms/elements/basic/button)
{% endcontent-ref %}

{% content-ref url="/pages/roPaGqlb1aiR9SRHZCOG" %}
[Image](/platform/build-forms/elements/basic/image)
{% endcontent-ref %}

{% content-ref url="/pages/zxQ79mwSG3JASgRFRWSr" %}
[Progress](/platform/build-forms/elements/basic/progress)
{% endcontent-ref %}

{% content-ref url="/pages/zr7B4G8H8JpA0fHoxTDE" %}
[Text](/platform/build-forms/elements/basic/text)
{% endcontent-ref %}

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr></tbody></table>


# Container

A Feathery Container is a box that can organize other elements within it into specific layouts. It can also be styled.

The `Container` component provides a container for organizing elements, styling and triggering actions.

It can be clicked to trigger a wide variety of [form actions](/platform/build-forms/actions).

You can give it a non-unique ID, to be sent back in [callback functions](/develop/react/api-guide/form/event-handlers) that it triggers or used in locating [custom components](/develop/react/api-guide/form/custom-jsx-components#customcomponents).

![](/files/ffIsUzwdTSTKsBR2eVcx)


# Button

A Feathery Button is clickable and contains text and an optional icon. It allows for detailed styling of states like hover, select, error, and more.

The `Button` component provides a clickable button for [triggering actions](/platform/build-forms/actions). You can also set various behaviors on it like showing loaders, triggering a CAPTCHA, setting disabled / error / selected / hover styles, and more.

<figure><img src="/files/ykWCNxYQn62PmUw973sA" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Creating a back button**&#x20;

Set the button click action to **`Go to Previous Step`**
{% endhint %}

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference button components with `type` equal to `button`


# Text

A Feathery Text allows the user to show free-form text and dynamic field values. It can optionally be clickable and supports detailed text styling.

The `Text` element allows you to add custom text to your form. [Text variables](/platform/build-forms/logic/display-dynamic-text) can also be included to show dynamic, custom text.

`Text` can also be clicked to trigger a wide variety of [form actions](/platform/build-forms/actions).

<figure><img src="/files/Zn5Q4uEcur0J1Qvjnxiw" alt="Screenshot of text element selected."><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference text components with `type` equal to `text`


# Progress

A Progress element will automatically track and show user progress as they progress through the form.

The `Progress` element displays the user's progress through a multi-step form. This can be displayed as either a bar or a stepper, depending on how you want to visualize the progress.

<figure><img src="/files/lOcXpOoTi59n6V7F8hqg" alt="" width="339"><figcaption></figcaption></figure>

### Bar

As a progress bar, the element will display a percentage indicating how much of the form has been completed. The percentage defaults to defaults to an automatic value based on the user's progress, but it can also be manually set in the **Properties** tab to a custom value.

<figure><img src="/files/it6DQaR91WPKmqtM8aD3" alt=""><figcaption></figcaption></figure>

### Stepper

As a stepper, the element will display a set of connected steps as well as the current active step. Previous steps are clickable, allowing the user to navigate backwards through the form.

Stepper elements across steps can be linked to each other, so you can maintain a single set of steps and labels across your entire form.

<figure><img src="/files/DWPPR8bqY1le6vYIbgqT" alt=""><figcaption></figcaption></figure>

## Styles

Aside from standard element styles, a set of progress-specific styles are available as well. You may configure the color of the progress element, if the text is shown or aligned, and the direction of the progress element - does it span horizontally or vertically.

<figure><img src="/files/joPERfc1L4H1GpzK47vY" alt="" width="515"><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference progress bar components with `type` equal to `progress_bar`


# Image

A Feathery Image will automatically show an uploaded or dynamic image on your form.

The `Image` component displays an uploaded image in your form.

![](/files/4WdnYmeKGaD2Ob4M2BtA)

### Image Source <a href="#example" id="example"></a>

You may directly upload the image to be displayed or you can have the image be dynamically pulled from a form or hidden field.

The image can be pulled from any field containing either an image file or a URL to an image file. This element also supports [displaying PDF files](/platform/build-forms/elements/basic/pdf-viewer).

<figure><img src="/files/ya4GxgnzZcqIrplRr7Px" alt=""><figcaption></figcaption></figure>

### Repeating Images <a href="#example" id="example"></a>

If the image element is repeating and is pulling its source image from either a repeating field or a hidden field containing an array of URLs, each repeat of the image element will pull its image from the corresponding value in the repeating field.

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference image components with `type` equal to `image`


# PDF Viewer

A Feathery Image will automatically show an user-uploaded PDF document on your form.

Use an [Image](/platform/build-forms/elements/basic/image) element to show the PDF that users upload to a file upload field.

<figure><img src="/files/dyfSkbheFKmQRctkLDWi" alt="" width="563"><figcaption><p>An image element, mapped to and conditionally rendered by, a file upload field.</p></figcaption></figure>


# Video

A Feathery Video element will automatically show a linked video on your form.

The `Video` component displays a video from a provided URL of either

* A third-party hosted video, like a Youtube or Vimeo video
* Your own custom-uploaded video, hosted somewhere like Amazon S3

For a custom-uploaded video, you can configure its autoplay/loop settings, video controls, and sound.

<figure><img src="/files/d0UhDh7gq1B2P7LBQOmt" alt=""><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference video components with `type` equal to `video`


# Table

The Table element allows you to display repeated fields and hidden fields in an organized layout.

The `Table` element displays field data as columns in a table layout.

<figure><img src="/files/dZIKrOEWpWJw42sSL2DR" alt=""><figcaption></figcaption></figure>

### Configuring Columns

Columns define what data is displayed in your table. Each column has:

* **Header**: The display name shown at the top of the column
* **Field Mapping**: The data field from your data source that populates this column

{% hint style="info" %}
Tables handle repeated fields automatically. If using hidden fields, make sure to use an array value to render each cell correctly.
{% endhint %}

### Configuring Actions

Actions are interactive buttons that appear at the end of each row, allowing users to perform operations on that row's data.

**To add/edit actions:**

1. Open the table's property panel
2. Navigate to the **Actions** section
3. Click "Add Action" to create a new action
4. Name your action (e.g., "View Details", "Edit", "Delete")
   1. This name is displayed to users and used to identify the action in code.

**Action Display Behavior:**

* **1 action**: Displayed as an individual button
* **2 or more actions**: Consolidated into an overflow menu (⋮) to save space

**Using Actions:**

See the Handling Row Click Events section below on instructions for using actions to run custom logic.

### Table Features

Enable or disable these features in the **Table Features** section:

* **Search Bar**: Adds a search input to filter table rows
* **Sort Options**: Allows users to sort columns by clicking column headers
* **Pagination**: Splits data across multiple pages with page controls

### Configuring Table Data

#### Repeated Fields

Tables work out of the box with repeated fields. Simply map your repeated field to the column and the table will display each value as a cell in a row.

#### API Connectors

Using the JSON mapping, you can map data to repeated fields and/or hidden fields. If you use the `*` notation, all data from the response will be added to the field value.

<figure><img src="/files/4JFSM2MG6h9ctCmtGZS1" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/7aCD166u46zmZ9qfye4k" alt=""><figcaption></figcaption></figure>

#### Custom Logic

You can use custom logic rules to assign repeated fields or hidden fields values. If you assign a hidden field an array of strings, it will display on the table.

```
table_field.value = ["value 1", "value 2", "value 3"];
```

### Editing Cell Values

When a table is configured to be editable, users can update cell values directly inline, the menu next to the cell is deprecated.

#### Editing a Cell

* **Click a cell** to start editing it. The cell turns into a text input pre-filled with its current value.
* **Enter** saves the change and closes the editor.
* **Shift+Enter** inserts a new line within the cell instead of saving.
* **Escape** cancels editing and discards any changes.
* **Clicking outside** the cell (blur) saves the current value.

#### Keyboard Navigation

While editing, use **Tab** and **Shift+Tab** to move between editable cells without reaching for the mouse:

* **Tab** saves the current cell and moves to the next editable cell to the right. At the end of a row, it wraps to the first cell of the next row.
* **Shift+Tab** saves and moves to the previous editable cell, wrapping to the end of the previous row.
* Navigation stops at the last editable cell (Tab) or the first editable cell (Shift+Tab) rather than wrapping around the whole table; your value is still saved.

### Handling Action Events

When a user clicks an action button in a table row, you can capture that interaction and trigger custom logic using the `feathery.trigger` object.

#### Available Trigger Data

When a row action is clicked, the following data is added to `feathery.trigger`:

* **`rowIndex`**: The index of the clicked row (accounts for pagination)
* **`rowData`**: An object containing the data from the clicked row in `{column header: value}` format
* **`action`**: The name of the clicked action (if applicable)
* **`columnIndex`**: The index of the clicked column.&#x20;
* **`columnKey`**: The field key mapped to the clicked column.&#x20;
* **`columnName`**: The header name of the clicked column.&#x20;

> **Note:** `columnIndex`, `columnKey`, and `columnName` are set only when a user clicks a specific data cell. They are absent for action-button clicks and row-level clicks.<br>

#### Setting Up Logic Rules

1. Create a new Logic Rule with trigger type **"Element Click"**
2. Select your table element as the trigger element
3. Access the trigger data in your conditions or actions using:
   * `feathery.trigger.rowIndex`
   * `feathery.trigger.rowData`
   * `feathery.trigger.action`&#x20;
   * `feathery.trigger.columnIndex`&#x20;
   * `feathery.trigger.columnKey`&#x20;
   * `feathery.trigger.columnName`&#x20;

### Advanced Usage

#### Row click events

You can trigger logic on row click by checking for the absence of `action` in `feathery.trigger` . If there is no action property, then the click was on the row and not an action button. Use this for step navigation or other use cases.

{% hint style="info" %}
If using row click actions, apply custom styling to the table to signify that the row is clickable to the user.\
\
Add the following custom html to a container on your step:

```
<style>
  tbody tr:hover {
    background-color: #e5e7eb;
    cursor: pointer;
  }
</style> 
```

{% endhint %}

#### Row Striping

Custom html can be used to add stripes to your rows:

```
<style>
  tbody tr:nth-child(even) {
    background-color: #f3f4f6;
  }
</style>
```

#### Cell click events

In addition to row-level clicks, you can respond to a click on a specific cell. When a data cell is clicked, `feathery.trigger` includes the column the user clicked, so you can branch your logic on which column was selected:

* `feathery.trigger.columnIndex` — the index of the clicked column
* `feathery.trigger.columnKey` — the field key mapped to that column
* `feathery.trigger.columnName` — the column's header name

Distinguish the three click types in your logic rule:

* **Action click** — `feathery.trigger.action` is present
* **Cell click** — no `action`, but `feathery.trigger.columnKey` (or `columnIndex`) is present
* **Row click** — neither `action` nor `columnKey` is present

### Legacy Tables

It's possible to build your own table layout using a combination of [containers](/platform/build-forms/elements/basic/container), [fields](/platform/build-forms/elements/fields), and (optionally) [container repeat logic](/platform/build-forms/logic/dynamically-repeating-containers). This method is more customizable than the table element and allows you to display fields and elements inside of cells.

<figure><img src="/files/YEzETf26lgaqiJn00XDv" alt=""><figcaption><p>An example of a simple table built on Feathery</p></figcaption></figure>


# Tooltip

A Feathery tooltip will show your user helper text when they interact with it.

Tooltips are not discrete elements in Feathery. Instead, once you've added a field like a [text field](/platform/build-forms/elements/fields/text-field) to your form, you can edit its tooltip text in the `Style` tab.

<figure><img src="/files/D546PotmwoqmopWUuRYf" alt=""><figcaption></figcaption></figure>


# Tabs

Let users navigate between form steps with labeled tabs.

The `Tabs` element lets users navigate form steps from labeled tabs. The tab for the current step appears active. Clicking a tab navigates to its linked step.

<figure><img src="/files/G3ZgoumwMFjRvsOVAAyx" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
When you add a Tabs element, Feathery can prefill tabs from nearby form steps. You can rename labels, change linked steps, and add or remove tabs in the **Properties** panel.
{% endhint %}

### Configuring Tabs

Open the Tabs element's **Properties** panel to manage its tabs.

Each tab has:

* **Label**: Text displayed on the tab
* **Step**: The form step opened when a user clicks the tab

{% hint style="info" %}
Each step can link to only one tab on a Tabs element. Add another step before adding another tab.
{% endhint %}

#### Validate and submit on click

Enable **Validate and submit on click** to save the current step's data before navigation. When disabled, a tab click navigates without submitting.

### Linking Tabs Across Steps

Link Tabs elements on different steps to share tabs and labels across your form. Editing one linked element updates every linked element.

{% hint style="info" %}
Adding Tabs to a form with existing tabs links it automatically when possible. You do not need to recreate the tabs on each step.
{% endhint %}

### Styles

Along with standard element styles, Tabs supports:

* **Direction**: Display tabs horizontally or vertically
* **Gap**: Set spacing between tabs
* **Default**, **Hover**, and **Active**: Set background, font, and border styles for each state. The active tab links to the current step.

### For Developers

The Feathery SDK and API reference Tabs components with `type` equal to `tab`.


# Fields

Feathery supports a comprehensive set of fields that are completely configurable and allow the user to enter a wide variety of data.

Gather the right data with our numerous field options.

![](/files/kWQmMkSpQQRiPfpzwHkk)

## How to Use Fields

#### **Styles**

Modify the element padding, scale, typography, and color styles.

{% hint style="info" %}
Scroll just past the Typography section to set the field placeholder.
{% endhint %}

![](/files/BKLDHqdFfQt3foHOp3qL)

#### **Properties**

Customize element constraints, display conditions, and more.

{% hint style="info" %}
Make fields optional in the Constrains section.
{% endhint %}

![](/files/V4aTWKnj6Fdo4yiVkOq3)

## For Developers <a href="#example" id="example"></a>

If you're trying to read or write a specific field type via the Feathery API or SDK, visit the docs for your field of interest to see how the field type should be formatted.

## Explore

{% content-ref url="/pages/Kf1rJ4xkaQXXePXpcYm0" %}
[Address](/platform/build-forms/elements/fields/address)
{% endcontent-ref %}

{% content-ref url="/pages/Nd4TRf55lwm2EggsxXOX" %}
[Button Group](/platform/build-forms/elements/fields/button-group)
{% endcontent-ref %}

{% content-ref url="/pages/wzjsH3zFEyDsTswEBHi6" %}
[Checkbox](/platform/build-forms/elements/fields/checkbox)
{% endcontent-ref %}

{% content-ref url="/pages/51Xbncjy7NzZL8cbP0Tv" %}
[Checkbox Group](/platform/build-forms/elements/fields/checkbox-group)
{% endcontent-ref %}

{% content-ref url="/pages/RLRIumASmd4c7wWcOCm1" %}
[City](/platform/build-forms/elements/fields/address/city)
{% endcontent-ref %}

{% content-ref url="/pages/xRQC4PdLFC1iQkx9JAYl" %}
[Color Picker](/platform/build-forms/elements/fields/color-picker)
{% endcontent-ref %}

{% content-ref url="/pages/dWig1qryvifdebvMatIM" %}
[Dropdown](/platform/build-forms/elements/fields/dropdown)
{% endcontent-ref %}

{% content-ref url="/pages/IcZhzG5bSc44Rs0b0T14" %}
[Email](/platform/build-forms/elements/fields/email)
{% endcontent-ref %}

{% content-ref url="/pages/bMfSEkFqYgPxFjnH8DYI" %}
[Number](/platform/build-forms/elements/fields/number)
{% endcontent-ref %}

{% content-ref url="/pages/6JNIxSDEVSUNZ7RUB8Bo" %}
[Phone Number](/platform/build-forms/elements/fields/phone-number)
{% endcontent-ref %}

{% content-ref url="/pages/1QwaF89t3DDk4GFzHJFW" %}
[Radio Group](/platform/build-forms/elements/fields/radio-group)
{% endcontent-ref %}

{% content-ref url="/pages/QPGb5wgkSXItIt1Peg98" %}
[File Upload](/platform/build-forms/elements/fields/file-upload)
{% endcontent-ref %}

{% content-ref url="/pages/3FVsUFWsai03PjoqAeoq" %}
[Signature](/platform/build-forms/elements/fields/signature)
{% endcontent-ref %}

{% content-ref url="/pages/AN8MRKtv6UXmLcrNwhnW" %}
[Social Security Number](/platform/build-forms/elements/fields/social-security-number)
{% endcontent-ref %}

{% content-ref url="/pages/E8XZchJydIyDDjwajRjV" %}
[State](/platform/build-forms/elements/fields/address/state)
{% endcontent-ref %}

{% content-ref url="/pages/gss1l7Oe9P7dfXGjPc6w" %}
[Text Area](/platform/build-forms/elements/fields/text-area)
{% endcontent-ref %}

{% content-ref url="/pages/85SZCB6thPOhDWy6ZVdh" %}
[Text Field](/platform/build-forms/elements/fields/text-field)
{% endcontent-ref %}

{% content-ref url="/pages/U1m5EXHXoYFN1w6rV69w" %}
[URL](/platform/build-forms/elements/fields/url)
{% endcontent-ref %}

{% content-ref url="/pages/sozwOydabbRnWZvrlKhq" %}
[Zip Code](/platform/build-forms/elements/fields/address/zip-code)
{% endcontent-ref %}

{% content-ref url="/pages/0jXiwmriUWrdjLE8O1mH" %}
[Payment Method](/platform/build-forms/elements/fields/payment-method)
{% endcontent-ref %}


# Address

The Feathery Address fields allow users to enter an autocompleted street address powered by Google Maps.

The address fields support collecting an international street address. The [Address Line 1 field](/platform/build-forms/elements/fields/address/street-address-line-1) displays a search-as-you-go dropdown of addresses that autocomplete the address fields when you select an option. The address data is powered by Google Maps.

From the Feathery API, we [return the Google Maps raw data](/develop/react/api-guide/form/event-handlers/onchange#triggerdata-object) when an address option is selected from the dropdown.

<figure><img src="/files/ciERfvrYofAtqMKV9aja" alt=""><figcaption></figcaption></figure>

### Related Fields

{% content-ref url="/pages/lPAfmIGNqnqmx7jN8IjX" %}
[Street Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1)
{% endcontent-ref %}

{% content-ref url="/pages/Wey0ECHUp4KXEICW4tl3" %}
[Street Address Line 2](/platform/build-forms/elements/fields/address/street-address-line-2)
{% endcontent-ref %}

{% content-ref url="/pages/RLRIumASmd4c7wWcOCm1" %}
[City](/platform/build-forms/elements/fields/address/city)
{% endcontent-ref %}

{% content-ref url="/pages/E8XZchJydIyDDjwajRjV" %}
[State](/platform/build-forms/elements/fields/address/state)
{% endcontent-ref %}

{% content-ref url="/pages/sozwOydabbRnWZvrlKhq" %}
[Zip Code](/platform/build-forms/elements/fields/address/zip-code)
{% endcontent-ref %}

{% content-ref url="/pages/m3thF1wi36iaQVjLDpV8" %}
[Country](/platform/build-forms/elements/fields/address/country)
{% endcontent-ref %}


# Street Address Line 1

The Street Address Line 1 field allows users to enter the first line of an autocompleted street address powered by Google Maps.

The `Address Line 1` field displays a search-as-you-go dropdown of addresses that autocomplete the address fields when you select an option. The address data is powered by Google Maps.

The `Address Line 1` field specifically collects the first line of a street address.

Typing into this field triggers a dropdown to appear, which allows the user to select an address from the list. If other address fields are present on the step (Address Line 2, City, State, Zip Code), those fields will also be populated with the relevant values from the selection.

Feathery uses the [Google Maps API](https://developers.google.com/maps/documentation/javascript/places-autocomplete) to support address autocomplete. We leverage our own API key for non-business tier volume, but we will ask business plan customers to provide their own key after a certain amount of volume.

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference address line 1 fields with `type` equal to `gmap_line_1`. The submitted value is a `string`.

To access the Google Maps API data, including address components and latitude / longitude, you can subscribe to the change event for this field and access [integrationData](/develop/react/api-guide/form/event-handlers/onchange#triggerdata-object).

### Related Fields

{% content-ref url="/pages/RLRIumASmd4c7wWcOCm1" %}
[City](/platform/build-forms/elements/fields/address/city)
{% endcontent-ref %}

{% content-ref url="/pages/E8XZchJydIyDDjwajRjV" %}
[State](/platform/build-forms/elements/fields/address/state)
{% endcontent-ref %}

{% content-ref url="/pages/sozwOydabbRnWZvrlKhq" %}
[Zip Code](/platform/build-forms/elements/fields/address/zip-code)
{% endcontent-ref %}


# Street Address Line 2

The Street Address Line 2 field allows users to enter the second line of an autocompleted street address powered by Google Maps.

The `Address Line 2` field collects the second line of a street address, such as an apartment number or suite.

If an address is selected from the dropdown of an [Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1) field on the same step, this field will automatically populate with the second address line.

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference address line 2 fields with `type` equal to `gmap_line_2`. The submitted value is also a `string`.

### Related Fields

{% content-ref url="/pages/lPAfmIGNqnqmx7jN8IjX" %}
[Street Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1)
{% endcontent-ref %}

{% content-ref url="/pages/E8XZchJydIyDDjwajRjV" %}
[State](/platform/build-forms/elements/fields/address/state)
{% endcontent-ref %}

{% content-ref url="/pages/sozwOydabbRnWZvrlKhq" %}
[Zip Code](/platform/build-forms/elements/fields/address/zip-code)
{% endcontent-ref %}


# City

The Feathery City field allows users to enter the city of an autocompleted street address powered by Google Maps.

The `City` field collects the city of a street address.

If an address is selected from the dropdown of an  [Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1) field on the same step, this field will automatically populate with the city.

Feathery uses [Google Maps API](https://developers.google.com/maps/documentation/javascript/places-autocomplete) to support address autocomplete.

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference city fields with `type` equal to `city`. The submitted value is a `string`.

### Related Fields

{% content-ref url="/pages/lPAfmIGNqnqmx7jN8IjX" %}
[Street Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1)
{% endcontent-ref %}

{% content-ref url="/pages/E8XZchJydIyDDjwajRjV" %}
[State](/platform/build-forms/elements/fields/address/state)
{% endcontent-ref %}

{% content-ref url="/pages/sozwOydabbRnWZvrlKhq" %}
[Zip Code](/platform/build-forms/elements/fields/address/zip-code)
{% endcontent-ref %}


# State

The Feathery State field allows users to enter the state of an autocompleted street address powered by Google Maps.

The `State` field allows the user to choose the state or province of a specified country from a list of options in a dropdown.

If an address is selected from the dropdown of an [Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1) field on the same step, this field will automatically populate with the state.

If a [Country](/platform/build-forms/elements/fields/address/country) field's value is selected on the step, the `State` field options will automatically update to reflect the regions of the specified country.

You may also configure the default country and region that is used by the `State` field.

Feathery uses [Google Maps API](https://developers.google.com/maps/documentation/javascript/places-autocomplete) to support address autocomplete.

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference state fields with `type` equal to `state`. The submitted value is a `string`.

### Related Fields

{% content-ref url="/pages/lPAfmIGNqnqmx7jN8IjX" %}
[Street Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1)
{% endcontent-ref %}

{% content-ref url="/pages/RLRIumASmd4c7wWcOCm1" %}
[City](/platform/build-forms/elements/fields/address/city)
{% endcontent-ref %}

{% content-ref url="/pages/sozwOydabbRnWZvrlKhq" %}
[Zip Code](/platform/build-forms/elements/fields/address/zip-code)
{% endcontent-ref %}


# Country

The Feathery Country field allows users to enter the country of an autocompleted street address powered by Google Maps.

The `Country` field allows the user to choose a country from a list of options in a dropdown.

If an address is selected from the autocomplete options of an [Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1) field on the same step, this field will automatically populate with the country.

Feathery uses [Google Maps API](https://developers.google.com/maps/documentation/javascript/places-autocomplete) to support address autocomplete.

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference country fields with `type` equal to `country`. The submitted value is a `string`.

### Related Fields

{% content-ref url="/pages/lPAfmIGNqnqmx7jN8IjX" %}
[Street Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1)
{% endcontent-ref %}

{% content-ref url="/pages/RLRIumASmd4c7wWcOCm1" %}
[City](/platform/build-forms/elements/fields/address/city)
{% endcontent-ref %}

{% content-ref url="/pages/sozwOydabbRnWZvrlKhq" %}
[Zip Code](/platform/build-forms/elements/fields/address/zip-code)
{% endcontent-ref %}


# Zip Code

The Feathery Zip Code field allows users to enter the city of an autocompleted street address powered by Google Maps.

The `Zip Code` field collects the zip code of a street address.

If an address is selected from the dropdown of an [Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1) field on the same step, this field will automatically populate with the zip code.

Feathery uses [Google Maps API](https://developers.google.com/maps/documentation/javascript/places-autocomplete) to support address autocomplete.

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference zip code fields with `type` equal to `zipcode`. The submitted value is a `string`.

### Related Fields

{% content-ref url="/pages/lPAfmIGNqnqmx7jN8IjX" %}
[Street Address Line 1](/platform/build-forms/elements/fields/address/street-address-line-1)
{% endcontent-ref %}

{% content-ref url="/pages/RLRIumASmd4c7wWcOCm1" %}
[City](/platform/build-forms/elements/fields/address/city)
{% endcontent-ref %}

{% content-ref url="/pages/E8XZchJydIyDDjwajRjV" %}
[State](/platform/build-forms/elements/fields/address/state)
{% endcontent-ref %}


# Button Group

The Feathery Button Group field allows users to create a group of selectable buttons with different values, labels, and icons.

The `Button Group` field allows the user to select an option from a group of buttons, each representing a selectable option. Each option can contain both text and images, and you can set the button group to either single-select or multi-select.

By default, the `height` and `width` styles will set a fixed dimension for each button in the group. However, you can make your buttons content-aware by toggling `Responsive Button Widths` on. This will treat your `width` as a minimum and grow buttons dynamically depending on the size of their content.

<figure><img src="/files/ZAtTD5uaqX62tpiuniZD" alt="Screenshot of a button group element selected."><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference button group fields with `type` equal to `button_group`. The submitted value is a `string`.


# Checkbox

The Feathery Checkbox field allows users to check a checkbox off or on.

The `Checkbox` field allows you to add a single checkable box, like you might see when accepting terms and conditions.

![](/files/9Vd9lABPT6qX8WZTOXY1)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference checkbox fields with `type` equal to `checkbox`. The submitted value is a `boolean`.


# Checkbox Group

The Checkbox Group field allows users to select several options from a group of options, one per checkbox.

The `Checkbox Group` field allows you to add a group of checkable boxes, which can be used for selecting multiple options at once.

You can also configure the `Max Selectable Options` property if you want your users to only be able to select a certain # of options within a range of possibilities. Once your user has selected the max # of options, the other checkboxes will automatically be disabled.

![](/files/RzMcLFqhloEc5PjSiyoh)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference checkbox groups with `type` equal to `multiselect`. The submitted value is an `array of strings`.


# Color Picker

The Color Picker field allows users to pick a color from a visual color palette.

The `Color Picker` field allows users to select a hex color and opacity via a graphic color selector.

![](/files/YhmjKwnnzqZEQ5XpOYbO)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference color pickers with `type` equal to `hex_color`. The submitted value is an `8-character string` where the first 6 characters are the hex color and the last 2 characters define the opacity.


# Combobox

The Combobox field allows users to enter freeform text with autocomplete options.

A combobox (text field + custom autocomplete options) can be implemented by adding a [Text Field ](/platform/build-forms/elements/fields/text-field)to your form and adding `Autocomplete Options` to the field from its properties tab.


# Dropdown

The Dropdown field allows users to pick an option from a series of options that display in a popup list.

The `Dropdown` field allows the user to choose one value from a predefined list of options.

![](/files/6sp9pH2r13XYSH3FFwkg)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference dropdown fields with `type` equal to `dropdown`. The submitted value is a `string`.


# Dropdown Multiselect

The Dropdown Multiselect field allows users to pick multiple options from a series of options that display in a popup list.

The `Dropdown Multiselect` field allows the user to choose multiple values from a predefined list of options.

<figure><img src="/files/JpzboAnQfXVNlFYsa5Xr" alt=""><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference dropdown fields with `type` equal to `dropdown_multi`. The submitted value is a `string`.


# Date Selector

The Date Selector field allows users to choose a date from a visual interface.

The `Date Selector` field allows the user to select a date, historical or future. The date value gets stored in the format `YYYY-MM-DD`. You can also configure the date selector to allow the user to pick a specific time of day. In that case, the entry is stored in the format `YYYY-MM-DDTHH:mm:ss`.

<figure><img src="/files/eB3c7WtDA189WdrJIvJZ" alt=""><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference dropdown fields with `type` equal to `date_selector`. The submitted value is a `string` of length 10, including `-` separators between the month, day, and year. If time selection is enabled, the submitted value is a string of length `19` and also includes the hour, minute, and second.


# Time Selector

The Time Selector field allows users to choose a specific time from a visual interface.

You can add a time selector field to your form by adding a [Date Selector](/platform/build-forms/elements/fields/date-selector), which you can configure to allow picking a specific time of day.

<figure><img src="/files/eB3c7WtDA189WdrJIvJZ" alt=""><figcaption></figcaption></figure>


# Email

The Email field allows users to enter a free-form email address.

The `Email` field accepts correctly formatted email addresses as answers such as <john.doe@gmail.com>.

A badly formatted entry will trigger a validation error on submission and require the user to fix the entry.

![](/files/kvZyxDrMukgsLIJccdGQ)

### Authentication <a href="#example" id="example"></a>

If you configure a Feathery auth integration, you can use the `Email` field with a `Button` action `Send Email Magic Link`.

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference email fields with `type` equal to `email`. The submitted value is a `string`.


# File Upload

The File Upload field allows users to upload one or multiple files, with constraints on file type and size.

The `File Upload` field allows users to upload a single or multiple files.

You can also configure the type of files allowed to be uploaded (e.g. documents, media, etc.) and specify a max file upload size as well.

If no max file size is specified, the field will have a default size constraint of 10MB, and at most 20 files can be uploaded to a single field. These constraints can be increased on the business plan.

![](/files/A4Sc7gRZH5GhvmHTzhHP)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference file upload fields with `type` equal to `file_upload`. The submitted value is a [file object](https://developer.mozilla.org/en-US/docs/Web/API/File).


# Matrix

The matrix field allows you to set up a group of questions that all share the same options.

The `Matrix` field allows you to set up a group of questions that all share the same options. They're represented in a tabular format, and you can also configure if your end-users can select multiple options per question.

<figure><img src="/files/csTcpXVGTQN83uvCKBRO" alt=""><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference pin input fields with `type` equal to `matrix`. The submitted value is of the format `{<question id>: [<selected option>]}`.


# Number

The Number field allows users to enter a free-form numerical value.

The `Number` field only accepts numbers as answers such as 1 or 1000. It will not accept non-numerical inputs.

You can also configure the field to follow certain formats, e.g. currency.

![](/files/HWUaW1lsmpGIJSbSH7h7)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference number fields with `type` equal to `integer_field`. The submitted value is an `int` or `float`.


# Password

The password field allows users to securely enter a password, with custom requirements.

The `Password` field collects a secure string that is encrypted visually and in Feathery storage. Users can specify custom password constraints, like max/min length, requiring at least 1 symbol, number, letter, etc.

<figure><img src="/files/gf8T7tmUWMUmAH2Thudv" alt=""><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference pin input fields with `type` equal to `password`. The submitted value is a `string`.


# Picture Choice

A picture choice field can be implemented with the button group field and uploaded images on each button.

A `Picture Choice` field can be implemented by using the [Button Group](/platform/build-forms/elements/fields/button-group) field and uploading images to each button.

Under `Properties`, go to the option section and create your desired number of options. Then, click the `Edit` icon on the right and select `Add Image` to add an image to that button.

<figure><img src="/files/ZAtTD5uaqX62tpiuniZD" alt="Screenshot of a button group element selected."><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference button group fields with `type` equal to `button_group`. The submitted value is a `string`.


# Pin Input

The Pin Input field allows users to enter a numerical pin code of a preset length.

The `Pin Input` field takes a numerical code, which can be used to complete the SMS verification process by entering a SMS verification code.

This field requires a Feathery auth integration to be turned on in order to integrate seamlessly, but you can also use this field to collect PINs for your own use.

![](/files/kcGQtY7MxMqimjwgWPPq)

### Supporting integrations

* [Firebase](https://www.feathery.io/integrations/firebase)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference pin input fields with `type` equal to `pin_input`. The submitted value is a `string`.


# Phone Number

The Phone Number field allows users to enter an international phone number with proper validation.

The `Phone` field accepts any phone number. whether domestic or international. It also validates the phone number to make sure it's valid.

A badly formatted entry will trigger a validation error on submission and require the user to fix the entry.

![](/files/9IxajCUVjgLuE9pvv5k6)

### Authentication <a href="#example" id="example"></a>

If you configure a Feathery auth integration, you can send an SMS code to the collected phone number. Just add a button with the click action `Send SMS Verification Code`.

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference phone fields with `type` equal to `phone_number`. The submitted value is a `string`.


# Radio Group

The Radio Group field allows users to pick one option among many, one per radio button.

The `Radio Group` field allows you to specify a group of radio buttons, where no more than one of the buttons can be selected at a time.

![](/files/pPzRcSuAYWD89CGeVFtS)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference radio group fields with `type` equal to `select`. The submitted value is a `string`.


# Rating

The Rating field allows users to pick a rating among a preset range of values, with a custom rating icon.

The `Rating` field allows the end-user to choose a rating via a range of available stars. The range and icon styles are customizable.

<figure><img src="/files/Ia58V9XwPjSekh99XGTY" alt="Screenshot of selected rating element."><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference radio group fields with `type` equal to `rating`. The submitted value is an `integer`.


# Signature

The Signature field presents a canvas that users can use to draw a signature.

The `Signature` field allows you to collect a signature from the user. Feathery allows you to collect legally-binding signatures.

## Signature Methods

You may toggle the available signature methods for users. They can either type, draw, or do both to generate their signature.

## Signature Workflows

You may want to use Feathery's [collaboration workflow feature](/platform/collaborative-workflows/collaboration) to invite multiple users to collaborate on a single form, collect multiple signatures in one submission, and restrict each user to filling out specific form sections.

Feathery [Documents](/platform/document-autofill-and-signatures) also allow you to collect e-signatures directly on documents with the same signature element.

![](/files/wu5MPHQdhm1EWeddvCJO)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference signature fields with `type` equal to `signature`. The submitted value is a `File`.

To trim the whitespace around the signature, it's best to perform server-side processing on your image file.


# Legally Binding Signatures

Electronic signatures that you collect through Feathery are valid and legally binding.

## Are electronic signatures legal?

The Electronic Signatures in Global and National Commerce Act, otherwise known as the E-Sign Act, states that [electronic signatures](https://www.adobe.com/sign/electronic-signatures.html) shouldn’t be considered invalid simply because they’re electronic. In other words, e-signatures are completely legal and binding. They can hold up in court.

This means the electronic signatures you collect are legally binding in every state where federal law applies. In addition, some jurisdictions have either developed their own legislation or adopted the Uniform Electronic Transactions Act (UETA).&#x20;

Feathery’s electronic signature solution is also fully compliant with UK electronic signature laws, including the Electronic Communications Act 2000 and the Law Commission’s 2019 report on Electronic Execution of Documents. Under these frameworks, electronic signatures collected through Feathery are legally binding and enforceable within the United Kingdom, provided there is clear intent to sign, identity verification, and document integrity. Feathery’s secure infrastructure, audit logging, and identity verification options ensure that each signature meets these requirements.

## Best practices for collecting e-signatures

To ensure that the electronic signatures you collect will hold up in court, it’s important to keep several factors in mind:

1. There must be a legitimate intent to sign. That means you can leave no doubt that the signer deliberately signed the document in question. Feathery's [signature field](/platform/build-forms/elements/fields/signature) asks users to use a cursor or touchpad to purposefully "draw" their name, which makes the intent clear. You can also include a required "consent" field to be selected before the signature can be submitted.
2. You must ensure there is consent to do business electronically. For example, if your business uses online forms to capture signatures, you can add a consent clause and require signers to "click to accept" before the electronic signature can be submitted.
3. It’s important to retain any records associated with the eSignature, such as a complete copy of the signed digital document. Feathery retains your data and signatures and can serve as your source of truth or sync to your source of truth as well.
4. Collect your signatures in a flow powered by a secure service. Feathery is SOC 2 compliant and encrypts your user data in transmission and at rest.
5. Verify the user's identity to ensure the signature is authentic. Feathery offers a number of ways to confirm a user's identity directly in your form flow.
   * [SMS one-time passcodes](broken://pages/Ju1PEPKFyu4JDZkk3uYh)
   * [Email magic links](broken://pages/4FPDZM0tNzechjKI57MB)
   * [One-click social logins](broken://pages/8qkfWVOKGPuyAvhRhd1W)
   * Government ID verification
     * Reach out to <support@feathery.io> for pricing
   * Custom verification logic
     * Build your own bespoke verification logic with [advanced logic and custom integrations](/platform/build-forms/advanced-logic) to third-party APIs.
6. Collect an audit trail to ensure the signatures are not tampered with. Feathery supports comprehensive audit logging of resource access and creation on our enterprise plan. Contact `support@feathery.io` for more information.


# Slider

The Slider field allows users to slide a knob between a custom range of values with a custom interval.

The `Slider` field allows the user to choose a number within a range of values by dragging a toggle left and right.

`min value` - the smallest (leftmost) value possible

`max value` - the largest (rightmost) value possible

`min label` (optional) - if you want to display a custom label for the smallest value

`max label` (optional) - if you want to display a custom label for the largest value

`step size` - the increment to change the value by while dragging.

<figure><img src="/files/5oYMyVyBAAChP2fTZ3WB" alt="Screenshot of a slider element selected."><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference number fields with `type` equal to `slider`. The submitted value is an `int` .


# Social Security Number

The Social Security Number field allows users to securely enter a social security number. This is especially useful for secure forms like a loan application.

The `SSN` field only accepts correctly formatted social security numbers of the form `xxx-xx-xxxx`, where x is a digit.

A badly formatted entry will trigger a validation error on submission and require the user to fix the entry.

![](/files/PgbSIRooKl9liM2KWRZk)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference social security number fields with `type` equal to `ssn`. The submitted value is a `string`.


# Text Area

The Text Area field allows users to enter a free-form, long text entry that can contain newlines.

The `Text Area` field allows you to collect a multi-line text answer. Maximum and minimum expected lengths can be set for the answer.

<figure><img src="/files/BKLDHqdFfQt3foHOp3qL" alt="Screenshot of a selected text area element."><figcaption></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference file upload fields with `type` equal to `text_area`. The submitted value is a `string`.


# Text Field

The Text field allows users to enter a freeform text entry with configurable options for allowed characters, length, mask, and more.

The `Text Field` field allows you to collect a single line text answer. The field can be configured to only accept certain characters, have a minimum and/or maximum length, and contain a custom prefix or suffix. It can also be configured to display autocomplete options in a dropdown as the user types in the field.

![](/files/lBbbMhwX7POlnMik7GOH)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference text fields with `type` equal to `text_field`. The submitted value is a `string`.


# Payment Method

The Payment Method field, backed by Stripe, allows users to securely enter a PCI-compliant credit card number.

Once the [Stripe integration](https://www.feathery.io/integrations/stripe) is activated and set up, the Payment Method field will become available to use for capturing payment information as part of a payment flow.  Presently, only credit cards are supported. &#x20;

This payment method field may be dragged anywhere onto your form and is the only way to enter payment/card information in your form to be passed securely to Stripe. The payment method field collects the card number, CVC, expiration month/year and postal code. It supports standard styling.

Once your end user fills the field and submits the step, the payment method will be automatically saved into the customer record in Stripe.

<figure><img src="/files/Q6OriU5zxcybB4qHhBOO" alt=""><figcaption><p>Payment Method in the Feathery designer</p></figcaption></figure>

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference payment method fields with `type` equal to `payment_method`. The submitted value is `json`.  The value of this field is read-only and not settable via the SDK.

Example value:

```
{
    card_data: {
        brand: "discover",
        last4: "1117",
        country: "US",
        exp_year: 2029
        exp_month: 9
    },
    stripe_payment_method_id: "pm_aD45hG60f3twY"
}
```


# URL

The URL field allows users to enter a free-form URL.

The `URL` field only accepts correctly formatted URLs as an answer such as `https://www.feathery.io/`.

A badly formatted entry will trigger a validation error on submission and require the user to fix the entry.

![](/files/lUYvxQ08K3eJCeIpLhUs)

### For Developers <a href="#example" id="example"></a>

The Feathery SDK and API reference URL fields with `type` equal to `url`. The submitted value is a `string`.


# Custom HTML, JS, CSS, and Iframes

You can add custom HTML, Javascript, and iframes directly to your Feathery form.

## Custom Components

You can add custom HTML components, Javascript, and iframes directly to your form. After selecting a [container](/platform/build-forms/elements/basic/container), you can configure its iframe URL or add custom code to embed. Both iframe URLs and custom code support dynamic values via [double-curly brace field syntax](/platform/build-forms/logic/display-dynamic-text).

To add custom CSS to specific elements, you can query them by their IDs and apply custom CSS to the retrieved elements.

<figure><img src="/files/hSKBHVei6QIKw6lbiCy4" alt="" width="358"><figcaption></figcaption></figure>

## Form Head Code

If you want to add HTML or JS header code to your form, you can do so in your form settings under the **Form Properties** tab.

<figure><img src="/files/7ML05BNafIbUCnhvsTQk" alt="" width="375"><figcaption></figcaption></figure>

## Form CSS

To apply CSS classes to your overall form, you can add the CSS within the **Form Properties** tab.

<figure><img src="/files/GNzfBN6sdSUig2nUzqIj" alt="" width="375"><figcaption></figcaption></figure>

## JSX Components

If you want to [add JSX components](/develop/react/api-guide/form/custom-jsx-components) to your embedded React form, you can do so from our [React SDK](https://docs.feathery.io/develop/react/api-guide/form/custom-jsx-components).


# Custom Fields

Build a custom field by using React code or composing multiple Feathery elements together.

While Feathery does allow you to embed custom HTML, JS, and CSS code, you can also set up custom fields directly in Feathery. You can do this by either specifying reusable field components via custom React code or composing them in our visual editor from basic building blocks.

{% content-ref url="/pages/PzLiKKFNKrFYzdtnd4hT" %}
[React Fields](/platform/build-forms/elements/custom-fields/react-fields)
{% endcontent-ref %}

{% content-ref url="/pages/epdecysBzBuO9VqfHcS6" %}
[Compose Elements](/platform/build-forms/elements/custom-fields/compose-elements)
{% endcontent-ref %}


# React Fields

Customize your forms with your own components written in React.

<figure><img src="/files/jEOT1nXamo9ddQGs8hVH" alt="" width="198"><figcaption><p>Selecting a custom component to add</p></figcaption></figure>

### Setting up a custom Feathery field

If you're on Feathery's growth or business plan, go to your account settings, and then click the **Custom Fields** tab. Specify the following information

1. Custom Field Name: user-friendly label for your custom field
2. Custom Field Type: internal, unique identifier for your custom field type
3. Custom Field Icon: URL to icon (preferably SVG) to use in the form designer for your custom field
4. Custom Field code: React component code to render your custom field

Your account and child workspaces (if on our white label plan) will subsequently have access to the custom field element that they can drag onto and use within their forms.

### Component Requirements

Your custom component must follow these requirements to work properly:

* Must be exported as the default export
* Must accept and handle `value` and `onChange` props
* Must be a valid React component

#### Example Component

{% code overflow="wrap" %}

```jsx
import React from 'react';

const Counter = ({ value = 0, onChange }) => {
  const handleClick = () => {
    onChange(value + 1); // Increments the current value
  };

  return (
    <div>
      <p>Current value: {value}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
};

export default Counter;
```

{% endcode %}

### Props

Your component will receive these props:

* `value`: The current value of the field. Can be a valid json value or object
* `onChange`: Function to update the field's value
  * Accepts one parameter: the new value
  * Updates the form state automatically
* `fieldProperties`: Object
  * `required`: Boolean
  * `disabled`: Boolean
* `formContext`: Object
  * `rightToLeft`: Boolean - The form's RTL writing mode setting
  * `editMode`: Boolean - Is true when inside the form builder and false when a user is filling the form.

### Using NPM Packages

You can import and use npm packages in your custom component. Here's a more complex example using the react library `react-colorful`

```jsx
import React from 'react';
import { HexColorPicker } from 'react-colorful';

const ColorPicker = ({ value = '#4287f5', onChange }) => {
  function getContrastTextColor(hexBackground) {
    // Remove # if present
    const hex = hexBackground.replace('#', '');

    // Convert hex to RGB
    const r = parseInt(hex.slice(0, 2), 16);
    const g = parseInt(hex.slice(2, 4), 16);
    const b = parseInt(hex.slice(4, 6), 16);

    // Calculate luminance using WCAG formula
    const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;

    return luminance > 0.5 ? '#000000' : '#ffffff';
  }

  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        gap: '20px',
        padding: '20px',
        maxWidth: '400px',
        margin: '0 auto'
      }}
    >
      <HexColorPicker color={value} onChange={onChange} />
      <div
        style={{
          padding: '20px',
          borderRadius: '8px',
          backgroundColor: value,
          color: getContrastTextColor(value),
          textAlign: 'center',
          transition: 'all 0.3s ease'
        }}
      >
        {value}
      </div>
    </div>
  );
};

export default ColorPicker;
```

<figure><img src="/files/T2gaMZrtVipat0g7vMyq" alt="" width="375"><figcaption><p>Custom Color Picker component</p></figcaption></figure>

### Current Limitations

Please note the following limitations:

* Style properties inside the designer are not currently supported
* Component must be self-contained in a single file
* Component cannot use Typescript

These limitations will be updated in the near future.


# Compose Elements

Build a custom field by composing multiple Feathery elements together.

You can also create custom Feathery fields directly from the visual editor by composing multiple Feathery elements.

## Examples

### Button group with custom layout

<figure><img src="/files/vpkXBkEhVSECUtiJIqBO" alt=""><figcaption><p>Feathery's default button group styles all options the same way. Using individual buttons allows for custom styling, such as the double-wide "Something else?" text field.</p></figcaption></figure>

If the default [button group](/platform/build-forms/elements/fields/button-group) field layout doesn't work for your use case, you can build your own field with a custom layout.

The field above is built with individual [buttons](/platform/build-forms/elements/basic/button) configured with the `Save Value to Field` click action. Specify the custom value to store on click, and then connect the button to a field where the value will be stored. If you want the user to only be able to select a single button, you can re-use the same field across all of the buttons in the field. If you want multi-select behavior, use a different field per button so they don't override each other on click.

<figure><img src="/files/x6uWGAHsmtuRgtcfT8qk" alt=""><figcaption><p>If the button click action is <code>Save Value To Field</code>, you can configure the value to store and the field to store the value into. The button will appear toggled when selected. Clicking the button again clears the value from the field.</p></figcaption></figure>

### Multi-select button group with card layout

<figure><img src="/files/92RQggzmRYupCj2LVLtv" alt=""><figcaption><p>You can use <a href="/pages/heEI0pXvNeNz3kkF6flO">Containers</a> to create unique card layouts.</p></figcaption></figure>

By setting the background color of a container and using the `Save Value to Field` click action, you are able to make a clickable card group consisting of any elements you desire. In the image above the first three cards are made with a checkbox field and a few text elements. The last card is just a checkbox and a text field.

<figure><img src="/files/WFrFKfFnU6XwLeoQZnZ1" alt=""><figcaption><p>If you select a checkbox as the field type there is no value to specify. The action will toggle the checkbox based on the previous value.</p></figcaption></figure>

### Dynamic Number of Buttons in Group

Branching off of the above example, you can create a single container and set it as repeating to allow it to repeat a dynamic number of times. Then, you can set the click action on the container to `Save Value to Field` and make sure to set the save field value to either a repeating field or a hidden field that contains an array of values.

Then, set the number of container repeats and set the save field value to an array with the same number of repeats. Now, when each repeating container is clicked, it will save the value corresponding to the same index in the save field array.

By default this is a multiselect action - each repeating container can be selected independently. You can turn on the **Single Select** option for the `Save Value to Field` action to ensure only one repeat of the field can be selected at once.


# Design

A guide for designing your Feathery form.

A guide for designing your Feathery form.


# Reuse styles and elements

Each form is backed by a theme, which is its design system. It allows you to avoid needing to restyle every similar element on your form.

Every Feathery form has an associated theme, which is its underlying design system. Instead of needing to manually restyle each new element on your form from scratch, the theme allows you to leverage a set of consistent styles and components across your entire form.

Once you change the style of an element, the style label will turn blue. Clicking the label will allow you to apply that style to all elements of a similar type or reset the element style to the original theme value.

<figure><img src="/files/gOQ72VS8bbttMNlSZiD5" alt=""><figcaption></figcaption></figure>

### Demo

{% embed url="<https://www.loom.com/share/70a3305852ce4b639f47ee55a95006de>" %}


# Styling

Themes follow an inherited styling pattern where you can set styles at a global and per-element level.

Form elements inherit their styles from the theme's styles. The theme styles can be controlled directly from the [**Theme Builder**](https://app.feathery.io/themes), or you can apply updates to the theme as you're building the form by selecting the blue highlighted style labels.

In the theme itself, you can set styles at several levels. Typography styles can be set globally, and any style can be set for specific element types.

<figure><img src="/files/4CHseNjlyIPS5tIrwW4r" alt=""><figcaption></figcaption></figure>


# Assets

Assets are form elements configured with certain sets of styles and properties. They can then be reused throughout your form.

**Assets** are form elements configured with certain sets of styles and properties. These custom-configured elements can then be reused throughout your form.

For example, you may configure different `Next` and `Back` button styles and want to use them interchangeably in your form. You would then create two assets in your theme, one for each style.

#### How to Create Assets

You can save assets to your form's associated theme for quick access.

1. Start with an element and make desired changes to its styles and properties.
2. Click the `+ Save as Asset` button that appears in the left panel.
3. View your saved asset by selecting the red number badge in grid view or switching to list view.
4. Publish your form to save the new asset to your theme.

![](/files/Fa8nQyFwN9wLKbujJ5i5)

#### How to Update Assets

You can update assets in a form or theme. Updates will apply to all assets in all forms using the same theme.

To update in a form:

1. Make desired changes to the asset's styles and properties.
2. Select the ↻`Update Asset` button in the left panel OR the individual style <mark style="background-color:blue;">Override Menu</mark> in the right panel.
3. Publish the form to finalize your asset update.

![](/files/xfDRsgRh8ry8YryFXYIJ)

#### How to Manage Assets

You can manage assets by using the `Overflow Menu` found in the left panel (open via `Right Click`) or in their associated theme. The theme will give you an overview of all your assets.

![](/files/pTRuiOIwf1oRaXLql6B6)


# Alignment and Spacing

Quickly align and space out elements in your form.

Elements and containers can be quickly aligned and spaced out using the **Styles** tab in the right panel.

### Alignment

#### How to align containers

You can align containers and all objects inside by modifying the **Layout** in the **Style** tab.

You'll see two kinds of alignment when you select a container;&#x20;

**`Align self`**: Use this to modify the position of the selected container.

**`Align content`**: Use this to modify all the objects inside the selected container without impacting the parent container. &#x20;

<figure><img src="/files/QqSjJwjfqX4i7QykwZDW" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Create a form body container nested inside your step container to easily align the form content and set a standard width. This is especially helpful for stand-alone forms that are not embedded.
{% endhint %}

#### How to align elements

If you want all the objects to have the same alignment...

* Select the parent container
* Modify `Align content` to apply one alignment to all elements inside the container.

If you want to give a different alignment to some objects...

* Select the element directly
* Modify `Align self` to override the container styles for the individual element

<figure><img src="/files/YgtH6LreUB0JhdX0kmht" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Not seeing the alignment you applied? Check the individual object and its parent container to see if an override alignment is applied. If an object has `align self` modified, it may not react to `align content`.
{% endhint %}

### Spacing

#### How to use gaps

You can evenly space out all objects inside a container using `Gaps`. A standard `Gap` can be added in the **Layout** section of the **Style** tab.&#x20;

Gaps allow you to evenly space out objects you have already placed and any new objects added to the container.&#x20;

<figure><img src="/files/pXloDHAGClBzPsrTyrT7" alt=""><figcaption></figcaption></figure>

#### How to use automated spacing

Use `Spacing` to set an automatic layout if you have multiple objects inside a container with its height set to `fill` or an exact `pixel` value.&#x20;

If your objects are in a `fit` container, use Gaps instead. Automatic spacing also will not impact containers with a single element or container in it. Add another object to see the spacing applied.

<figure><img src="/files/OiRA2b7JXlu916KatDoM" alt=""><figcaption></figcaption></figure>

### Demo

{% embed url="<https://www.loom.com/share/a6fb7df0dd774535985f7f4b1c6f26e4>" %}


# Sizing Forms and Elements

Flexibly size your elements and overall form using fixed and responsive units.

Forms and elements on the forms can be sized using the following units.

### Fit

Containers will resize automatically to fit any content placed inside, making automatic layouts a breeze.

### Fill

Enables containers and groups to grow to fill the view, allowing for centered forms and more.

### Pixels (PX)

Set the dimensions of a container or group to an exact numeric pixel value.

{% hint style="info" %}
If a form sized in pixels is greater than its screen, it will shrink to fit accordingly.
{% endhint %}

### What dimensions should I use for my form?

Use `Fit` if you plan to embed your form in a responsive container. Great for adding a form to a  new page on your website.

Use `Fill` if you want a stand alone full screen form. Perfect for sharing a link straight to the your form.

Use exact `PX` if you plan to embed your form in a fixed scale container. Best for fitting a form on your website page that already has other content.&#x20;

![](/files/5qPgy7KwyYheDtgtb005)

### What dimensions should I use for my containers and elements?

Use `Fit` if you want the container to be responsive to your element and maintain an exact scale regardless of the screen size.

Use `Fill` if you want the container to grow as wide as the screen size allows. Great for 100% scale elements on mobile.

Use exact `PX` if you want a fixed container for your element. Great for 100% scale elements on desktop.

{% hint style="info" %}
Use multiple **`fill`** containers to create equal spacing.&#x20;

Percentage elements will take priority over fill elements. For equal spacing, place your **`100%`** element inside a **`fill`** container next to other fill containers.

*Ex. 2 fill containers in a group = 50% each. 4 fill containers in a group = 25% each.*
{% endhint %}

![](/files/xYLqBsSDSvUfQoolntzx)

### Demo

{% embed url="<https://www.loom.com/share/7f3ed08fce3f489cb60fbcbbe237a659>" %}


# Responsive Layouts

Feathery forms have built-in responsiveness that is also customizable by you at a granular level.

Feathery forms will automatically adjust your layout as much as possible to fit a mobile viewport. You can also easily create a custom mobile design by toggling to the mobile viewport in the designer.

<figure><img src="/files/2xDaXCbwEegcFTB3iTwY" alt="" width="187"><figcaption></figcaption></figure>

Note that in the mobile view, any styles you set on your form will be **mobile-only**.&#x20;

{% hint style="info" %}
In the mobile view, any styles you set will be **mobile-only**. To clear out mobile-only styling, click on the blue highlighted label of the mobile-only style and click **Reset to Desktop**.
{% endhint %}

Your mobile layout and desktop layout will stay linked unless you adjust the dimension of a container. You can easily re-link your layouts in the mobile step panel.

<figure><img src="/files/yqOZhCISwyOBE4UN6y3Y" alt=""><figcaption></figcaption></figure>

**Rotate Axis**

You can quickly create mobile optimized layouts by rotating a container's axis. That action to `Rotate into rows` or `Rotate into Columns` can be found in the right click overflow menu and in the properties panel of the container.

<figure><img src="/files/nzbHnOiY9KTjy9gLRg5r" alt=""><figcaption></figcaption></figure>

**Element & Container Styles**

Set custom mobile styles for width, height, padding, font size, and more.&#x20;

{% hint style="info" %}
Toggle basic elements **visibility** to hide or swap out designs on mobile.
{% endhint %}

<figure><img src="/files/jtA484dLFjp2DdSOuHa2" alt=""><figcaption></figcaption></figure>

### Demo

{% embed url="<https://www.loom.com/share/9b4b4d4f9d42461a827f2a38b4f0b991>" %}


# Actions

When a user clicks buttons, text, or containers on your form, you can trigger a wide variety and sequence of form actions to occur.

When a user clicks [buttons](/platform/build-forms/elements/basic/button), [text](/platform/build-forms/elements/basic/text), or [containers](/platform/build-forms/elements/basic/container) on your form, you can trigger a wide variety and sequence of actions to occur.

<figure><img src="/files/qBTR06m2HHU4dVCQDpa0" alt=""><figcaption></figcaption></figure>

## Available Actions

### Validate & Save Fields on Step

This action runs before any other action you specify. It will validate all of the field entries on the current step and short-circuit any subsequent actions if it catches a validation error (missing field value, failed custom validation rule, etc.)

If validation completes successfully, it will save the field data to Feathery.

### Form Navigation

* Go to next step
  * Allows you to navigate to a different step of your form according to your [navigation rules](/platform/build-forms/logic/navigate-steps-conditionally#navigation-rules)
  * Optional allow validating and submitting the fields on your current step
  * If validation fails, navigation will not occur.
* Go to previous step
  * Navigates back to the last step that the user was on.

### Save a Field Value

You can configure an element to save a custom value into a form or hidden field when it is clicked. This allows you to:

* Track which elements were clicked for calculating navigation, validation, purchases, and more.
* Build custom fields by combining buttons and containers that store custom values into the same hidden field.

<figure><img src="/files/nDjHKLQ41Ev8Ujq0SScn" alt="" width="324"><figcaption></figcaption></figure>

If the button with the `Save Value to Field` action is [repeating](/platform/build-forms/logic/dynamically-repeating-containers) and the corresponding value you're saving is a repeating field or a hidden field storing an array of values, the corresponding index of the field value will be saved based on the repeat order of the button.

### Open a Link

Clicking an element can trigger a custom URL to open, either in the same window or in a new tab.&#x20;

Links also support field variable interpolation. You can use `{{<field_id>}}` syntax within the link you define, and Feathery will automatically replace it with the current session's field value before opening the link. To insert the user ID of your current session, use `{{feathery_user_id}}`

### Adding or Removing a [Repeating Container](/platform/build-forms/logic/dynamically-repeating-containers)

If you've set a container on your form as repeating, clicking an element with the `Add Repeating Container` action will cause a new copy of the container to appear right below the current one. You can keep triggering the action to continue adding more repetitions after each other.

Clicking an element with the `Delete Repeating Container` action will have the opposite effect. If the element is in the repeating container itself, the action will remove the repeating container that contains the element. Otherwise, specify the container that it applies to and it will remove the last repetition when clicked.

### Trigger Integrations

Certain integrations like [Plaid ](https://www.feathery.io/integrations/plaid)or [Stripe](https://www.feathery.io/integrations/stripe) can be triggered via a click. When Plaid is triggered, the Plaid modal will appear to guide the user through a flow for collecting their bank information. Stripe allows you to trigger an action to collect a payment from the user.

### Authentication Actions

When an [authentication integration](broken://pages/SlDs7xrqgGifHjWUMHOV) like [Stytch](https://feathery.io/integrations/stytch) or [Firebase](https://feathery.io/integrations/firebase) is connected to your form, you have access to verification actions that can be triggered via a click. This includes:

* Log in or sign up users [via SMS](broken://pages/Ju1PEPKFyu4JDZkk3uYh), [magic link](broken://pages/4FPDZM0tNzechjKI57MB) or [social login](broken://pages/8qkfWVOKGPuyAvhRhd1W).
* Log out users that have been logged in

### Start New Submission

If you have `Remember User` turned on in your form settings, you can still allow users to manually initiate new submissions by specifying `Start New Submission` as a click action. When your user clicks the element, it will trigger a new form session to begin that corresponds to a new submission and new tracked user.

Under the hood, this refreshes the cookie being used to track and identify the form user.

### Custom Action

Specifying a custom action to run won't do anything automatically. However, if you have a [logic rule ](/platform/build-forms/advanced-logic)specified that runs when an element is clicked, or if you've embedded your form and specified a [custom action](/develop/react/api-guide/form/event-handlers/onaction) as a callback function, your logic will be run when the user clicks the element.

### Invite Collaborator to Submission

This is useful if you're [starting a collaborative submission directly](/platform/collaborative-workflows/collaboration/start-directly). This allows you to specify the other collaborators on your submission via the form itself. You'll need to specify the field to pull their email from, along with which collaborator they map to (defined via your [collaboration template](/platform/collaborative-workflows/collaboration#define-your-collaborator-settings)). You won't be able to use this action unless you've defined your collaboration template with more than one collaborator.

### Verify Collaborator Email

This action will error and short-circuit if the field you associate with this action does not contain the email of a collaborator on the submission. This is useful if you want to set up an initial validation step so even if someone is able to access a collaborative submission link, they won't be able to proceed until they validate the email of the user it belongs to.

### Return to Previous Collaborator

For example, you might be the second reviewer in a sequence and have caught a mistake from the previous form filler. This action allows the reviewer to send a form back to an earlier collaborator in the sequence so they can review and re-edit their form entry.

If the collaboration sequence is ordered, the collaborator flow will restart from the collaborator specified in the return action and go through every collaborator after them as well.

If the collaboration sequence is unordered, only the collaborator specified in the return action will need to re-review their submission.

### Generate Documents

Autofill a set of document templates. Afterwards, you can either route the documents for signature or download them directly from the form.

If routing for signature, the form will automatically redirect the user to a document signature flow where they'll be able to sign the documents you just generated with form data.

You may also enable the **Repeatable** toggle, which will allow you to fill a single document template multiple times if the data you're filling in is repeating.

### Document Extraction

Trigger a document extraction to process and extract information from an uploaded file.\
\
If the extraction has variants set up, you can choose one to use. \
\
If Run Async is checked, the form will not wait for the extraction to finish and the extraction results will not be available in the form (the extraction will still show up in the extraction results page).


# Logic

Feathery supports powerful logic features that allow you to completely personalize your form experience.

Feathery's logic features allow you to personalize your form experience for individual users based on their data (answers, behavior, external info).

{% content-ref url="/pages/XzH64PoN1yXxOW4Eplac" %}
[Navigate Steps Conditionally](/platform/build-forms/logic/navigate-steps-conditionally)
{% endcontent-ref %}

{% content-ref url="/pages/6qR3VG83BHvBvZ7pJ9ri" %}
[Show Elements Conditionally](/platform/build-forms/logic/show-elements-conditionally)
{% endcontent-ref %}

{% content-ref url="/pages/1SRYlRWGZUimYFMmW9gT" %}
[Custom Field Validation](/platform/build-forms/logic/custom-field-validation)
{% endcontent-ref %}

{% content-ref url="/pages/lQKApO86sPQDXBHftxhr" %}
[Display Dynamic Text](/platform/build-forms/logic/display-dynamic-text)
{% endcontent-ref %}

{% content-ref url="/pages/zaVkDevViyedgffWa2OO" %}
[Dynamically Repeating Containers](/platform/build-forms/logic/dynamically-repeating-containers)
{% endcontent-ref %}

{% content-ref url="/pages/wotWvQKtnoNu2ailqTK0" %}
[Advanced Logic & API Connections](/platform/build-forms/advanced-logic)
{% endcontent-ref %}


# Navigate Steps Conditionally

Create conditional rules for how the user navigates from one step of your form to another.

### Steps

A `step` is the core building block of a form and can be thought of as a page. By default, every Feathery form begins with a single step, to which you can add components and fields, style, and assign a name (e.g. a contact form).

From there, you can create new, additional steps—from scratch or by duplicating existing form steps—and connect these steps to create multi-step forms (e.g. a customer onboarding flow).

In Feathery, steps can be found in the form builder and are visually represented as nodes. Navigation between steps can be built by hovering over the edge of a step until the "+" cursor appears, and then clicking and dragging a connecting line from that step to another.

Once a connection between steps has been created, it can be clicked to assign navigation rules that must be met to move the form user between the two steps.

![](/files/yh5IGjEoqTFC8wvNnAwE)

### Navigation Rules

Each navigation rule is made up of a target and conditions.

#### Target

A `target` is a form element (e.g. button or field) or the form itself that can be interacted with in order to trigger step navigation. This could be a clickable element (such as a button) or a field where changing its value triggers navigation to the next step.

#### Condition

A `condition` is a field value comparison that must be met in order for step navigation to trigger a particular rule. For example, the criteria could be a particular field value being equal to a preset value, or if the user has been authenticated or not.

![](/files/Pg6qPwp01TdAWE6HtCYI)

See the detailed discussion of [Logic Conditions](/platform/build-forms/logic/available-conditions).

### Conditionally Skip a Step

This is a common use case when you have an initial step where the user answers a set of questions that determine which of the following steps they should see. On each of the following steps, you can set a navigation rule that skips the step conditionally.

<figure><img src="/files/n4Ks4qIhG1uqIEIPHj0h" alt=""><figcaption></figcaption></figure>


# Show Elements Conditionally

Conditionally hide and show different form elements depending on your field values.

When editing a form element or container, you can specify conditions for when the element  should be hidden from the end-user in the `Show or Hide Conditionally` section. This is useful for filtering collections of items and hiding and showing:

* Additional questions
* Answers to questions
* Supplemental information

You can access this interface by clicking on an element or container in the `Designer`, and then going to its properties tab in the right sidepanel and clicking `+ New Rule` under the `Show or Hide Conditionally` section.

<figure><img src="/files/79FQmQsdLNjuqT9ru5yT" alt=""><figcaption></figcaption></figure>

See the detailed discussion of [Logic Conditions](/platform/build-forms/logic/available-conditions) for a list of comparator options and what they do.


# Custom Field Validation

Create custom logic for comparing and validating field values.

Many Feathery fields come with default validation rules. For example, an email field will validate that its input is a valid email on submission.

You can create additional custom rules to implement other validation checks. For example, you may want to make sure that the user enters different values in two different fields.

<figure><img src="/files/h4gwV4UChUOsSx3bPl2M" alt=""><figcaption></figcaption></figure>

Custom validation rules can be added to any field or button, and they will be run when the step is submitted. If the rule evaluates true, the custom validation message will appear on the element you added the rule to. Step submission and navigation will not occur until the validation error is resolved.

For more complex rules involving multiple possibilities, you can combine multiple comparisons using `AND` and `OR` combinators.

See the detailed discussion of [Logic Conditions](/platform/build-forms/logic/available-conditions).


# Available Conditions

Feathery supports building conditional logic for navigating between steps, hiding / showing elements, and custom field validation rules..

Conditional logic allows you as the designer to determine when a navigation, validation or hide rule will run.  The conditional logic is evaluated by the Feathery form at the appropriate time and if it evaluates true, the navigation happens or the validation sets the field/button invalid or the field/element/container is hidden.

Conditional logic (sometimes called rules) is built of logic expressions.  Each logic expression has a left side which is a field on the form and a comparison operator.  Depending on the comparison operator chosen, the logic expression may have a right side that can be either form field(s) or directly entered value(s).  The right side can also be made up of multiple fields/values that are OR'd together.  For example in the image below, the first logical expression reads as: if bus\_company's selections include Greyhound or Megabus then the condition is true.

Multiple logic expressions may be AND'd as shown in the screenshot below to form the condition/rule.

<figure><img src="/files/3VlcwBf4jTadpHdmCj1D" alt=""><figcaption><p>Two logic expressions composed into a validation rule</p></figcaption></figure>

The table below describes the supported logic comparison operators and where the apply.

| Comparison Operator                                                            | Purpose                                                                                                                                                                                                                      | Additional Info                                                                                                                                           |
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| is                                                                             | Evaluates true if the left side is equal to the right side of the comparison expression.                                                                                                                                     |                                                                                                                                                           |
| isn't                                                                          | Evaluates true if the left side is **NOT** equal to the right side of the comparison expression.                                                                                                                             |                                                                                                                                                           |
| is greater than, is greater than or equal, is less than, is less than or equal | Evaluates true if the left side is >, >=, < or <= to the right side of the comparison expression.                                                                                                                            | This is primarily used for numeric data and fields, but will work for text as well.                                                                       |
| is filled                                                                      | Evaluates true if the field on the left side of the comparison expression has data entered in it.                                                                                                                            |                                                                                                                                                           |
| is empty                                                                       | Evaluates true if the field on the left side of the comparison expression has **NO** data entered in it.                                                                                                                     |                                                                                                                                                           |
| is true                                                                        | Evaluates true if the field on the left side of the comparison is checked/selected.                                                                                                                                          | Applies only to single checkbox fields                                                                                                                    |
| is false                                                                       | Evaluates true if the field on the left side of the comparison is **NOT** checked/selected.                                                                                                                                  | Applies only to single checkbox fields                                                                                                                    |
| contains, contains (ignore case)                                               | Evaluates true if the text in the field on the left side of the comparison contains the text or the text in the field on the right side of the comparison.                                                                   | Can be used with text type fields such as text field, text area, email, phone number and address fields as well as drop downs, selects and button groups. |
| doesn't contain, doesn't contain (ignore case)                                 | Evaluates true if the text in the field on the left side of the comparison **does not** contain the text or the text in the field on the right side of the comparison.                                                       | Can be used with text type fields such as text field, text area, email, phone number and address fields as well as drop downs, selects and button groups. |
| starts with                                                                    | Evaluates true if the text in the field on the left side of the comparison begins with the text or the text in the field on the right side of the comparison.                                                                | Can be used with text type fields such as text field, text area, email, phone number and address fields as well as drop downs, selects and button groups. |
| doesn't start with                                                             | Evaluates true if the text in the field on the left side of the comparison **does not** begin with the text or the text in the field on the right side of the comparison.                                                    | Can be used with text type fields such as text field, text area, email, phone number and address fields as well as drop downs, selects and button groups. |
| ends with                                                                      | Evaluates true if the text in the field on the left side of the comparison ends with the text or the text in the field on the right side of the comparison.                                                                  | Can be used with text type fields such as text field, text area, email, phone number and address fields as well as drop downs, selects and button groups. |
| doesn't end with                                                               | Evaluates true if the text in the field on the left side of the comparison **does not** end with the text or the text in the field on the right side of the comparison.                                                      | Can be used with text type fields such as text field, text area, email, phone number and address fields as well as drop downs, selects and button groups. |
| is numerical                                                                   | Evaluates true if the text in the field on the left side of the comparison is a number.                                                                                                                                      |                                                                                                                                                           |
| is text                                                                        | Evaluates true if the text in the field on the left side of the comparison is **not** a number.                                                                                                                              |                                                                                                                                                           |
| selections include                                                             | For multi-select type fields, this operator evaluates true if **any** of the selections in the field on the left side of the comparison are equal to the text or the text in the field on the right side of the comparison.  | Applies only to multi-select checkbox fields and button groups.                                                                                           |
| selections don't include                                                       | For multi-select type fields, this operator evaluates true if **NONE** of the selections in the field on the left side of the comparison are equal to the text or the text in the field on the right side of the comparison. | Applies only to multi-select checkbox fields and button groups.                                                                                           |


# Display Dynamic Text

Display dynamic text on your form that changes depending on the value of a form or hidden field.

## Overview

You might want to show dynamic text on a step of your form depending on the collected form data or a custom logic rule. Alternatively, you might want to open a dynamic URL from your form or show a dynamic field tooltip.

To do so, you may include a text variable in your text with double curly braces like: `{{firstName}}`.

If the text variable matches the ID of a form or hidden field, Feathery will automatically populate it with the corresponding value. To populate the user ID of the current session, use `{{feathery_user_id}}`.

Aside from directly filling out fields in the form, you can also [manually set](/develop/react#setvalues) field values through [logic](/platform/build-forms/advanced-logic) or the [embed library](/develop/react) to populate text variables.

### Example <a href="#example" id="example"></a>

For example, you might need to display the input values from a calculator, along with the calculated total.

![](/files/n68dCckoKNarnafK0CFJ)


# Dynamically Repeating Containers

Have a form container and its contents dynamically repeat a number of times.

### Overview <a href="#example" id="example"></a>

A step in your form may need to show a variable number of containers and elements depending on the user who is filling out the form. For example, you might use this to show multiple loan offers or allow the user to enter a sequence of information. In this case, you can take advantage of our Repeating Containers feature.

### Video Demo

<https://www.loom.com/share/c75da44eb0b34d5184ba378b647b2fa1>

### Setup <a href="#example" id="example"></a>

1. In your Feathery visual editor, navigate to the step where you want to show a variable number of containers.&#x20;
2. Select the container and in properties (on the right sidebar), select the "Repeatable" option.

<figure><img src="/files/9j4HAk2ShM98RqqYpxep" alt=""><figcaption></figcaption></figure>

3. Set all fields within the repeatable container to also be repeatable by clicking each field, and then setting them to repeatable in the properties section of the right side bar.

<figure><img src="/files/xX1VfEDqDGlI3Jqrs5lW" alt=""><figcaption></figcaption></figure>

### Usage <a href="#example" id="example"></a>

Now that the container is allowed to repeat, you can trigger it in two ways.

1. Add two button elements to your step with the repeating container. Set the click action of the first button to `Add a Repeating Container` and the click action of the second button to `Remove a Repeating Container`. When your end-user clicks the first button in your form, another repetition will be added. When your end-user clicks the second, a repetition will be removed. **Note** that the `Remove` button can be placed anywhere when you specify which repeating container it will remove from. If placed in the repeated container, it will remove the specific repetition where it was clicked. The `Add` button can be placed anywhere, and you can specify which repeating container it will add.
2. Set the value of a field(s) in the container to a list of repeating field values. Your form will automatically infer from the length of the list how many repetitions of the container are needed. Repeating field values can be [set from the SDK](/develop/react#setvalues) programmatically or with [advanced logic](/platform/build-forms/advanced-logic).

### Logic <a href="#example" id="example"></a>

For Logic rules triggered by clicking a repeated container or object within the repeated container, you can use **`feathery.trigger.repeatIndex`** to know which repeated container object was clicked. This will return the index of the clicked object, starting with 0 for the first repeated container

### Example <a href="#example" id="example"></a>

You're building a financial app offering loans to your users. On the last step of your form, you want to show your user a variable number of offers, one in each container.

From the dashboard, you create a hidden field called `offer-description` and set it as a text variable in a container of the the last step of your form. Then, you set that container as `repeatable` from the right sidebar.

Finally, from our SDK that you installed in your codebase, you run the following code to show your user two different offers.

```
import { setValues } from '@feathery/react';

setValues({'offer-description': ['My first offer', 'My second offer']})
```


# Advanced Logic & API Connections

Have total control of your form at specific interaction points, including submissions, errors, and more.

Advanced logic is a powerful way for you to run custom rules at critical moments when the user interacts with your Feathery form. They allow you to completely control and modify the default behavior of the form. &#x20;

You configure these rules to run on certain events, such as when a step of your form is loaded, a step is submitted, a field value is changed, and more. The events available here are the [same ones](/develop/react/api-guide/form/event-handlers) available to the embed SDK.

The custom logic feature is available via the `Logic` tab in the dashboard.  You can add, modify and delete custom logic rules.

<figure><img src="/files/d428Fs9jzZEH7Uc72Lv3" alt=""><figcaption></figcaption></figure>

You can edit a custom logic rule by selecting it and either configuring our visual rule builder or adding custom JavaScript for advanced use cases.

## Visual Rule Builder

With the convenience of the visual rule builder, you can configure the logic of your rule without needing to know how to code.

The rule builder exposes a visual interface that allows you to choose when the rule should run, if it should run, and what should happen when it does run. Actions such as setting the value of a field, connecting to an API, opening a URL, and more are possible. It also supports an **expression builder** for setting up calculations and field value transformations.

<figure><img src="/files/mLdesG2B1UcKfPrZ0Awp" alt=""><figcaption></figcaption></figure>

Once you save your changes, the rule builder will automatically convert your rule to JavaScript code that can be seen in the "Code Editor" tab.

<figure><img src="/files/224moFLZuvvabPBxqIyM" alt=""><figcaption></figcaption></figure>

It's important to note that each rule is either built using the visual rule builder or with JavaScript. You can switch which method is used at any time. However, you cannot use both at the same time on a single rule.

[Read more about building rules using the visual rule builder here.](/platform/build-forms/advanced-logic/visual-rule-builder)

## Advanced Logic Using JavaScript

The visual rule builder is limited when it comes to advanced logic such as nested conditions, advanced calculations, etc. Using JavaScript allows you to have more control over your rule's logic.

[Read more about building rules in JavaScript here.](/platform/build-forms/advanced-logic/javascript-rule-builder)

## Field Access

By default, your rules will only have access to fields on the current form or hidden fields. However, you can turn on access to fields on other forms by enabling `Enable all form fields` at the top of your list of rules. Note that this may potentially cause performance issues if you have a **lot** of forms and fields.

<figure><img src="/files/tkSO5kvlTJGEfp9UdZgL" alt="" width="363"><figcaption></figcaption></figure>

## Backend API Requests

The rule logic you define will run on your end-user's browser. You may want certain network requests in your rule logic to be performed from Feathery's servers instead for security reasons. This can be done with [API Connectors](/platform/build-forms/advanced-logic/javascript-rule-builder/api-connections-in-code).


# Visual Rule Builder

Configure logic without needing to write code

## What is Feathery's No-Code Rule Builder?

Rule Builder allows you to define custom form logic in a natural language, without having to write any code.&#x20;

This includes the following:

* Conditionally setting certain field values or flags
* Navigating to steps in form or external webpages
* Conditionally showing or hiding elements
* [Calling APIs](/platform/build-forms/advanced-logic/visual-rule-builder/connect-to-api-action)
* Calculations
* Sending one time password via SMS and Telesign voice call (after corresponding integration is enabled)
* Custom validation (coming soon)

## Key Concepts

Each rule has multiple **branches**, which are independent statements within a rule.

Each branch include an **action** (a change in the form, like setting a field value) and a **condition** (when the action is triggered).&#x20;

All branches of rules also share the same **trigger**, which is when the list of rules are run (ex: on a certain form step being completed or field value changing).

If you configure a rule with an action but no condition, it will just run each time the trigger condition is met (such as a certain form step being completed)

Here's an example: let's say I want to create a rule that automatically sets a text field for the State each time a City dropdown option is selected.&#x20;

I'll create a **rule** called "Prefill State" and set the trigger for the rule to be whenever the "City" dropdown field value changes.&#x20;

In my rule, I'll have a single branch. The condition will be if the City dropdown is set to "San Francisco". For this condition, I'll create an action that sets the State field value to "CA". I can add a  more conditions based on any other Cities/States I want to support.&#x20;

<figure><img src="/files/IkUFZv43M5Y51RBNK1nR" alt=""><figcaption><p>Prefilling State based on which City is selected</p></figcaption></figure>

## How to access Rule Builder

Select your Feathery form, and then click on the "Logic" Tab. Click "Create Rule" to create a rule and select the Trigger (when you want the Rule to run).

<figure><img src="/files/ZzZy5ZgqwEPzYFLRRce8" alt=""><figcaption><p>Logic Tab</p></figcaption></figure>

<figure><img src="/files/Sueh0DM15RFZPhAFzpa1" alt=""><figcaption><p>Creating a rule</p></figcaption></figure>

## Getting Started

After creating a new rule, you will be presented with an empty rule and the ability to add your first action.

<figure><img src="/files/c8iIYqhVQnViLkZLkNRA" alt=""><figcaption></figcaption></figure>

To get started, add your first action to the rule by clicking "Add Action".

## Changing action types

By default, new actions are always a "Set" action which means setting a field value. You can change this by clicking the 3 dots on the action name which will open a menu with various other actions.

<figure><img src="/files/VDjkx6anImXDJwBcKttI" alt=""><figcaption></figcaption></figure>

## Adding additional actions

To add another action, click the "Add Action" button beneath the set of actions you want to add to. The actions will run in the order that they are added once triggered.

<figure><img src="/files/RU5jJxwkMwQzGWXivxM5" alt=""><figcaption></figcaption></figure>

## Adding a condition

If you only want to run the set of actions depending on another field's value, you can add a condition using the "Add Condition" button above the set of actions. If no condition is set, the action will run whenever the rule is triggered.

<figure><img src="/files/xLBLuyHhlxZboFBhCIBv" alt=""><figcaption></figcaption></figure>

To add an additional condition, you can click the "Add Condition" button again. By default, groups of conditions are part of an "And" clause meaning each condition must be true to trigger the actions. However, you can change this to "Or" by clicking the "And" found between the conditions.

## Adding a clause

Once you have a condition added to your set of actions, you can add an additional clause which by default is an "Else" statement meaning otherwise, do these actions.

<figure><img src="/files/baX9bdbWpsGuBw48I8sm" alt=""><figcaption><p>"Else" was added by clicking "Add Clause"</p></figcaption></figure>

You can add conditions to this additional clause as well.

## Adding a branch

Branches are useful when you have a conditional action but also want to run another action every time the rule is triggered. To add a branch, you can click the "Add Branch" found at the bottom of the rule builder.

<figure><img src="/files/xrWTahVM0D1xW2TsN2ww" alt=""><figcaption><p>An additional branch was added to the bottom by clicking "Add Branch"</p></figcaption></figure>

## Generated Code

Once you save changes on the rule builder, you can view the generated code by navigating to the "Code Editor" tab.

It's important to note that you cannot edit the code if you are configuring the rule using the rule builder. Clicking "Edit Code" will opt the rule into using JavaScript which means the rule builder will no longer be used.

Please also note certain actions are not supported in the code editor and a reminder will pop up after trying to switch to Advanced Logic. Please remove those actions before using the code editor.&#x20;

<figure><img src="/files/kQfn9ebzvmk7kklQ20Sy" alt=""><figcaption></figcaption></figure>


# Connect to API Action

Send or receive data to/from any API without having to write code

Feathery's No-Code API Connectors, an extension of our no-code Rule Builder, allows you to send or receive data to/from any API without having to write code, all within any Feathery form or workflow.&#x20;

### Example Use Cases:

* Dynamically rendering options for a drop-down
* Looking up a user in a database or CRM (ex: Salesforce, HubSpot) and prefilling their information
* Sending form data to your custom API endpoints
* Running custom validation logic from an API (ex: verify if a user's identity is valid)

### Video Demo

{% embed url="<https://www.loom.com/share/9a8b8df8cb0d4578b2511b68fb188c74>" %}

### How to access

Within the Logic tab of your form, create a Rule within Feathery's No Code Rule Builder. Then, within a Rule, you can define an Action for "Connect to API" which kickstarts the No Code API Connector flow.

<figure><img src="/files/5OAU7nHIptdhKH6Sw9sl" alt=""><figcaption></figcaption></figure>

### 1. Define the endpoint &#x20;

First, go to the Logic tab of your form, click "API Connectors" and press the "Create API Connectors" button.

Set the endpoint you want to use and any headers you want to send. Please refer to your intended API's documentation to determine which endpoint and headers to use.  You must also set what type of API request you need to make, such as a GET request for receiving information into your form or a POST request for sending form data to an API.&#x20;

You can also define what data you want to send to the request. You can reference field values for these as well using the {{field\_name}} syntax.

Any data you define in the headers or query parameters (such as sensitive API keys) will not be exposed client side to people filling your form.&#x20;

<figure><img src="/files/UyUnRZuhaeJSU9x3hltB" alt="" width="375"><figcaption></figcaption></figure>

For POST requests, you can define the request body that you want to POST to your endpoint. The format is a key (as a string) followed by a string value, with any variables included within the string enclosed in double curly braces, such as:

`{ "sample_key" : "{{sample_field_id}}" }`

Make sure each field is defined and published first before referencing the field in the request body.

<figure><img src="/files/5NV28SIJ1jweNgaokT3i" alt=""><figcaption><p>Example POST request</p></figcaption></figure>

### 2. Define the API response format

You can define the API response format in two ways:

**Option 1: Make a test API call**

You can run a test API call with the parameters you defined in the previous step to see the result. If you used any field values for headers or query parameters, you can define them prior to the test API call being run.

<figure><img src="/files/OimCgAe7DmEfArnbfwRG" alt="" width="375"><figcaption></figcaption></figure>

<figure><img src="/files/3BLIiiJcgKk02TMZ3ohi" alt="" width="375"><figcaption></figcaption></figure>

**Option 2:** Input a sample JSON response

You can also paste a sample response (ex: from the API docs) in case you may not be able to call the API just yet

### 3. Map the API result to form fields

You can configure how to extract data from the API's response. You can configure which fields you want to map to and which parts of the API response to pull from (ex: certain fields in the respones or indices of an array).&#x20;

Feathery supports pulling and mapping data from nested response fields and lists/arrays.

For example, in the screenshot below, we are mapping the "salesforce-full-name" field to the "Name" field of the first element "records" key in the response json, which is represented by the syntax "records.0.Name" (where 0 represents the 0th/first index of the array/list). You can use the dropdown to see all available mappings.&#x20;

You can also map multiple fields at once. In this case, we are mapping both the Account Name and Full Name fields.

<figure><img src="/files/sm0Q2y9ypW0iQfk8NRQ7" alt=""><figcaption></figcaption></figure>

### 4. Use the No-Code API Connector in a Logic Rule

Go to the Logic tab of your form and create a Rule. Set the action of your rule to "Connect to API" and select the API connector you created in the previous step. Then, the API connector will run when the rule's trigger is activated.

In the example below, we connect to the Salesforce API whenever the "salesforce-email" field is changed.

<figure><img src="/files/7SYXU6fMXUliaVqeRCPO" alt=""><figcaption></figcaption></figure>

## Variables

When defining and using your connectors, you may also have access to backend environment variables such as authentication tokens. You may define these via double curly braces in either the API connector headers or the url being passed into the `feathery.http` interface.

The authentication token for **any connected integration** can now be used in API connectors. The syntax to reference the auth token is `{{integration-name_token}}.`

Example variables that you have access to include (but are not limited to):

* `{{feathery_user_id}}` - the unique ID of the current submission / form end-user
* `{{feathery_form_id}}`- the ID of the current form
* `{{<Integration Type>_token}}` - auth token of your integration, if active. This can be `salesforce_token` , `hubspot_token`, `emoney_token`, `google-sheets_token`, etc.
* `{{FeatheryDocument:<Document ID>}}` - this allows you to include a link to a generated / e-signed document envelope.
* `{{feathery_auth_email}}` - the authenticated email address of your current user, if an auth integration like [Stytch](https://feathery.io/integrations/stytch) or [Firebase](https://feathery.io/integrations/firebase) is turned on

## Pass File Binary via URL

To pass the binary of a file directly as the request body, you may define the request body in the specific format `{"FeatheryBinaryData": "<URL>"}`, where the URL points to a file whose content should be downloaded and passed directly as the request body.

The `Content-Type`request header will automatically be set to the type of the file. If you specify a query parameter with the value `{{feathery_file_name}}`, that will automatically be set to the name of the file (with its file extension).

You can also use the format `{"FeatheryBinaryData": ["<URL>"]}` where an array of URLs are specified. In this approach, the API request will be issued multiple times, once with each URL's binary data passed. The responses will be returned in an array to the logic rule that initiated the connector, while the returned field values will be the combined values from all of the responses.

For example, you could pass `{"FeatheryBinaryData": "{{MultiFileUpload}}"}` where `MultiFileUpload`corresponds to the ID of a multi-file upload field in Feathery. This would allow passing the binary of each uploaded file within the field.

## IP Address Whitelist

API connector requests will always originate from `54.177.134.134`, so you can whitelist this specific IP address on the recipient endpoint.


# Salesforce API Connector

Feathery offers pre-configured Salesforce API connectors that allow users to write custom SOQL.

If you have Feathery's [Salesforce integration](https://www.feathery.io/integrations/salesforce) enabled on a form, you can build custom Salesforce API connectors that leverages your integration connection.

<figure><img src="/files/rLnKA09P1UVtaZXXZRlK" alt="" width="375"><figcaption></figcaption></figure>

The Salesforce API connector allows you to configure a custom SOQL query to prefill data from your Salesforce instance into your Feathery form. The SOQL query can leverage data from your existing Feathery fields, and the data that is returned can also be used to populate various fields.

<figure><img src="/files/kKEwNL1fXaj2AqbABrtj" alt=""><figcaption></figcaption></figure>

Similar to other API connectors, Salesforce API connectors can be used in any logic rule, at any point in your form flow. You can also switch to **Advanced Configuration** mode at any point to have full control of the API request being sent to Salesforce.


# Set Field Value Action

A set field action requires the selection of a form field and a "value" that will be set into the field.

A set field action requires the selection of a form field and a "value" that will be set into the field.  The "value" can be either:

* A specific value that may be a number, some text, true/false, etc.
* An expression that combines specific values and/or fields using arithmetic/text/logic operators and optionally parenthesis.

To support the above possibilities, set field actions support both a value entry "mode" and an expression entry "mode".  Set field actions will always start in value entry mode.  In value entry mode you may simply enter a number or text or if the field is a select type field, select one of the select options. &#x20;

<figure><img src="/files/uA3manzHsG3DAv6DW3CZ" alt=""><figcaption></figcaption></figure>

The set field actions have an overflow menu (3-dot icon) that has an option to put the input into expression input mode (see above).  In expression input mode, enter specific values and/or select fields and combine them with operators and parenthesis as needed.

Here are some very simple examples:

<figure><img src="/files/sdXxDTDOMygcnDFOju5X" alt=""><figcaption><p>Example Rule Builder numeric expression</p></figcaption></figure>

<figure><img src="/files/87G8djKH4SeNJw9cP4tl" alt=""><figcaption><p>Example Rule Builder Expression combining text</p></figcaption></figure>

## Expression Operators

The following operators are supported in expressions.  Parenthesis are also supported for numeric expressions.

| Operator | Name                  | Purpose                                               | Example                    |
| -------- | --------------------- | ----------------------------------------------------- | -------------------------- |
| +        | Addition              | Add two numeric sub-expressions                       | a + b                      |
| -        | Subtraction           | Subtract two numeric sub-expressions                  | a - b                      |
| \*       | Multiplication        | Multiply two numeric sub-expressions                  | a \* b                     |
| /        | Division              | Divide two numeric sub-expressions                    | a / b                      |
| +        | Concatenation         | Concatenate two text sub-expressions                  | firstName + " " + lastName |
| ==, ===  | Equality              | Compare two sub-expressions for equality              | a == b                     |
| !=, !==  | Inequality            | Compare two sub-expressions for inequality            | a !== b                    |
| >        | Greater than          | Compare two sub-expressions for greater than          | a > b                      |
| >=       | Greater than or equal | Compare two sub-expressions for greater than or equal | a >= b                     |
| <        | Less than             | Compare two sub-expressions for less than             | a < b                      |
| <=       | Less than or equal    | Compare two sub-expressions for less than or equal    | a <= b                     |


# Navigate Step Action

The Navigate to Step action allows you to set up conditional navigation rules that take the user from one step of the form to another, directly in logic.

The `Navigate to Step` action allows you to set up conditional navigation rules that take the user from one step of the form to another, directly in logic.


# Set Field Error Action

The Set Error action allows you to set an error for a specific field on the form.

The `Set Error` action allows you to set an error for a specific field on the form. If this action is run on a `submit step` rule, it will short-circuit the submission and prevent the user from progressing until the error is fixed.


# Open URL Action

The Open URL action allows you to open a URL conditionally, either in a new tab or replacing the current window.

The `Open URL` action allows you to open a URL conditionally, either in a new tab or replacing the current window. You can also include field values in the URL by specifying their field ID wrapped with double curly braces, e.g. `{{MyField}}`.


# Set Calendly URL Action

The Set Calendly URL action allows you to dynamically set the Calendly URL for an embedded Calendly scheduling flow.

This action is only available if you've enabled the Calendly integration for the form.

This action is useful if you want to conditionally show different Calendly flows or show multiple Calendly flows to a user.


# Javascript Rule Builder

Build complex, powerful custom rules with JavaScript

## Getting Started

By default, every rule starts out on the visual rule builder which means you must opt into using JavaScript.

You can opt-in to using JavaScript by navigating to your rule, clicking the "Code Editor" tab and then clicking "Edit Code" which will prompt you about opting into using JavaScript.

Once you convert your rule to using JavaScript, you can go back to the visual rule builder at anytime by clicking the "Rule Builder" tab.

**Note:** Opting back into the visual rule builder will restore the last state of the rule builder meaning any changes to the JavaScript code will be lost.

## Feathery Context

The `feathery` object exposes the Context API to your rule's logic. Using this object, you can perform actions such as setting a field value, navigating to a step, setting custom errors and more. [Full reference](/develop/context-api).

## Accessing Form Fields

If the field's ID is a valid JavaScript variable name, then you can access the field directly through a field global that is pre-defined for your convenience.

For example:

```javascript
// Interacting with a field using it's ID
myTextField.value = "some interesting text";

// Another example
MySelect.options = ["First Choice", "Second Choice"];
```

**Note**: Field globals are only available in logic rules running inside Feathery, not in SDK call-backs running outside of Feathery.

If the field ID is not a valid JavaScript variable name, then you can access the field using `feathery.fields` which is an object containing all keys.

For example:

```javascript
// Interacting with a field that have invalid JavaScript variable names
feathery.fields["1st_name"].value = "first name";

// Another example
feathery.fields["last name"].value = "last name";
```

Each method of accessing the field will result in a `Field` object being returned. You can read more about the available methods and properties on the Field object [here](/develop/context-api/field-object).

## Rule Runtime

Rule are run synchronously in that a rule must complete before feathery runs other rules or continues with form processing.  However, network requests made by rules may be either done from within the browser or on the [server](/platform/build-forms/advanced-logic/javascript-rule-builder/api-connections-in-code).  In either case, the network request is asynchronous.  If you want that network request to complete before continuing, you must await the request call.  See an example of awaiting a network request [here](/platform/build-forms/advanced-logic/javascript-rule-builder/api-connections-in-code#example).


# API Connections in Code

## Overview

The rule logic you define will run on your end user's browser. You may want certain network requests in your rule logic to be performed from Feathery's servers instead for security reasons, such as hiding authorization information from the user. To do so, you can leverage `API connectors`, which allow you to run API requests securely from Feathery's servers.

To create a connector, go to `API Connectors` on the `Logic` page and define the API you would like your rule to use. You need to define the method (ex: get, post), and any headers or query parameters you'd like to automatically include for all requests to that endpoint.

Any data you define in the headers or query parameters (such as sensitive API keys) will not be exposed client side to people filling your form.&#x20;

<figure><img src="/files/I6gGwrHjZnRmPGhDPFdn" alt=""><figcaption><p>Defining API Connector</p></figcaption></figure>

After defining the API you want to use, you will be asked to map the response. If you intend to use our Javascript editor to make the API calls, then you can skip this step by pressing the "Skip" button. If you want to use our no-code API connectors feature (which is what this screen below is for), click [here for relevant documentation. ](/platform/build-forms/advanced-logic/visual-rule-builder/connect-to-api-action)

<figure><img src="/files/TwUHgnRANqjcX2LYGQ8p" alt=""><figcaption><p>Skip step (unless you want to use no-code API connectors)</p></figcaption></figure>

Then, go to your rule code and make a network request to the API connector via the `feathery.http` interface. By defining the API connector, it will automatically pass in the relevant connector parameters (headers, body, parameters, etc.) on the server side in a secure manner.

<figure><img src="/files/wEjg9yccsqqyJdVWHwqS" alt=""><figcaption><p>API connector request example via Javascript code</p></figcaption></figure>

## Usage

You can use the `feathery.http` interface in one of two ways - a simple connector name-based approach and a more advanced, customizable approach.

### Simple

The simplest way to call the API connector is to run `feathery.http.connect(<connector name>)`where the name of the connector is what you specified when creating it. This allows for no additional customization of data on top of the connector.

### Customizable

In this approach, you call the connector via `feathery.http.<method>(<url>, <data>, <headers>)` . The right connector to use will be inferred based on matching the method and URL passed in to the method and URL defined on API connectors themselves. The URL passed into this request must contain the defined URL of the relevant API connector as a substring.

* `method` is a standard REST method: `get`, `post`, `put`, `patch`, `delete`
* `url`is an endpoint you're requesting that contains a defined API connector URL as a substring.
* `data` is the request body, which you should pass in an `Object` or `Array` format. This is merged with the request body defined on the connector config itself, if specified.
  * For example, `{email: <email>, name: <name>}`
* `headers` are additional headers you'd like to pass in an `Object` format. These are also merged with headers defined on the connector config itself.
  * For example, `{Authorization: 'Bearer <token>', 'Accept-Encoding': 'gzip'}`

For `get` requests, make sure to append query and URL parameters directly to the URL rather than attempting to set it via `data`, which is only used for the request body.

## IP Address Whitelist

All API connector requests originate from the static IP address listed below depending on the region your form is located at. You may whitelist this IP address to improve the security of your endpoint that's receiving Feathery API connector requests.

```javascript
US: 54.177.134.134
CA: 15.157.16.158
```

## Variables

When defining and using your connectors, you will also have access to [backend variables](/platform/build-forms/advanced-logic/visual-rule-builder/connect-to-api-action#variables) that can be included via double curly braces.

## Example

You're looking to fetch a list of dog breeds via `https://dog.ceo/api/breeds/list/all` from Feathery's servers. First, create the API connector.

<figure><img src="/files/KOEzashAvVKdL7Hp3fpP" alt=""><figcaption></figcaption></figure>

Then, use the `feathery.http` interface to access the API connector from your logic rule.

<figure><img src="/files/wl7TfX0gpD6rHTnkZ4qt" alt=""><figcaption></figcaption></figure>


# Trigger Integrations from Logic

On our Business Tier, you can trigger integrations using Feathery's Javascript Rule Builder. \
This allows you to trigger these integrations at any point of your form and under any conditions that you set.&#x20;

Here are the steps to trigger integrations:

1. In the integration tab of your form, define an integration action (such as our Salesforce integration) using the `Custom Logic` trigger. An integration action is a single action for a given integration (ex: Create / Update for a certain object type in Salesforce)

<figure><img src="/files/I4CZlJ12reWivGaYvO1x" alt=""><figcaption></figcaption></figure>

2. Then, once you press Save, an ID will appear at the top of the integration. You can copy that ID (or multiple IDs)
3. Then, within a Logic rule, you can use the no-code "Run integrations" action or  [feathery.runIntegrationActions](/develop/context-api) function to trigger the integration.&#x20;
   1. For the on-code action, use the "Run Integrations" action in the no-code logic builder and select which integration(s) you want to run. The "wait for completion" toggle determines whether you want to wait for the integration to finish before proceeding to the next step of the logic rule

      <figure><img src="/files/rUjW3Nbjb7ZPj0ImQH4G" alt=""><figcaption></figcaption></figure>
   2. If you want to use code, the function takes in the following parameters:
      1. `actionIds`: string\[]: an array of all the integration IDs that you want to run in order
         1. `options`: JSON with the following attributes:
            1. `waitForCompletion`: boolean. Sets whether you want the form to wait until the integration is done running before proceeding. If true, the form will wait. If false, the integration will run asynchronously and let the user continue in the form.
            2. `multiple`: boolean. Sets whether you want to be able to run this integration multiple times for a single submission. If set to true, you can run an integration multiple times. Otherwise, if false, a given action can only be run once per submission.
            3. Here is some sample code to trigger two Salesforce integration actions:&#x20;

               `let accountCreation = await feathery.runIntegrationActions(['b1802192-d7be-4432-9916-e62f17d8b23a','93b378b5-34ec-4ae3-a644-e160a79486c2'], {"waitForCompletion": true, "multiple": false} );`

<figure><img src="/files/Npr5jLGLwLMx3F3hHBX4" alt=""><figcaption></figcaption></figure>

4. Integrations triggered from Logic also support **dependencies**. For example, creating a Salesforce account and then a contact that is associated with that newly created account. For Salesforce actions, you can enable this by using the "Create or Update Record" Salesforce action, and then selecting the object type you want to create/update (ex: Contacts, Accounts, Custom Objects, etc). Then, under the "Linked Object Fields" section, you can see all of the related object fields for the chosen object type. You can link those related object fields to the objects created or updated by any other integration action.&#x20;
   * To do this, in the field dropdown, there are options for `ID - Action <Action_ID>`. These map to any previously created Salesforce integration actions.&#x20;
   * When you link these fields, then when this integration is run, it will reference the ID of the created Object associated with the action of the field.

<figure><img src="/files/88JXrYEIgdWSILHrsu5B" alt=""><figcaption></figcaption></figure>

5. In the example below, I am calling two actions in order:  `b1802192-d7be-4432-9916-e62f17d8b23a` ( which creates a Salesforce Account) and `93b378b5-34ec-4ae3-a644-e160a79486c2` (which creates a Salesforce Contact).&#x20;
   * In the second action that creates the Contact, I have defined the Account ID field (found in the Linked Object Fields header) to link to `@ID- Action b1802192-d7be-4432-9916-e62f17d8b23a`. This means that the created Contact will be linked to the newly created/updated Account from the first action.&#x20;
   * I then need to make sure I call these actions in the right order within my Logic rule. I need to ensure that the Action ID for the Create/Update Account action comes first before the Contact action.
   * Code Snippet: `let accountCreation = await feathery.runIntegrationActions(['b1802192-d7be-4432-9916-e62f17d8b23a','93b378b5-34ec-4ae3-a644-e160a79486c2'], {"waitForCompletion": true, "multiple": false} );`


# Sharing Code

Create Javascript functions and constants that can be shared across your logic rules.

To keep your code clean, you can add small, testable JavaScript modules containing functions and constants that can be accessed from multiple Logic Rules. A Shared Code module exports named constants and functions. Logic Rules import those exports and use them to drive form interactions.

### Why use shared code

* Share common helpers across many forms and rules.
* Keep Logic Rules lean and focused on orchestration.
* Improve testability and readability.

### How it works

* You author a Shared Code module in its own code editor.
* You export named constants and functions from that file.
* In a Logic Rule, you import the names you need from the Shared Code by its key.
* You call the imported functions or read the constants, then set field values as usual.

### Authoring rules and constraints

Use only named exports. The editor validates your code and blocks unsupported syntax.

**Allowed**

* `export const NAME = ...`
* `export const fn = (...args) => { ... }`
* `export function NAME(...args) { ... }`

**Disallowed**

* Default exports, for example `export default ...`
* Aggregate or aliased exports, for example `export { a, b as c } [from '...']`, `export * from '...'`
* Variable exports with `let` or `var`, for example `export let x = ...`, `export var x = ...`
* Destructured constant exports, for example `export const { a } = ...`, `export const [a] = ...`
* Type level exports, for example `export class`, `export enum`, `export interface`, `export type`

**Notes**

* Every `export const` must have an initializer.
* Prefer pure functions. Avoid relying on ambient globals or browser state.
* Keep exports small and single purpose.

### Creating Shared Code

1. Create a new Shared Code module and give it a clear key, for example `Logic-1`.
2. Add your exports in the editor.
3. Save. The validator will flag unsupported patterns before save.

### Video walkthrough contents

The accompanying video demonstrates the full workflow end to end.

* Create a Shared Code module with named exports.
* Save and confirm validation passes.
* Open a form’s Logic Rules and import  from the Shared Code module name.
* Wire Rule 1 to a button to randomize two TextFields.
* Wire Rule 2 to a button that reads an index from a TextField and writes the selected color to a Hidden field.
* Run both interactions to verify values update as expected.

{% embed url="<https://feathery.s3.us-west-1.amazonaws.com/docs/Shared_Codes_Example.mp4>" %}

**Example Shared Code module**

```js
// Exports a few helpers for colors and fruits.

const favoriteColors = ['red', 'orange', 'green', 'blue'];
const fruit = ['apple', 'blueberry', 'cherry', 'strawberry'];

export function randomColor() {
  const index = Math.floor(Math.random() * favoriteColors.length);
  return favoriteColors[index];
}

export const randomFruit = () => {
  const index = Math.floor(Math.random() * fruit.length);
  return fruit[index];
};

export function chooseColor(value) {
  const colorIndex = Number(value);

  if (!value || colorIndex < 0 || colorIndex > favoriteColors.length - 1) {
    return '?';
  }

  return favoriteColors[colorIndex];
}
```

### Importing into a Logic Rule

Use the Shared Code Module name as the source. Import only the names you need.

#### Example 1: Randomize two TextFields on button click

**Rule code**

```js
import { randomColor, randomFruit } from 'Logic-1';

Value_Color.value = randomColor();
Value_Fruit.value = randomFruit();
```

**What it does**

* Each click of the “Random Color and Fruit” button runs the rule.
* `Value_Color` and `Value_Fruit` TextFields update with new random values.

#### Example 2: Map a user input index to a color

**Rule code**

```js
import { chooseColor } from 'Logic-1';

selected_color.value = chooseColor(Value_Color_Index.value);
```

**What it does**

* Each click of the “Choose Color” button runs the rule.
* The rule reads the `Value_Color_Index` TextField, converts it to a number, then calls `chooseColor`.
* The result is assigned to a Hidden field named `selected_color`.

### Import source and naming

* Use the Shared Code name as the import source string, for example `'Logic-1'`.
* If the key changes, update imports in any Logic Rules that reference it.

### Validation behavior and common errors

* **Default export used**\
  Replace with a named export. Example: `export function helper() {}`.
* **Export without initializer**\
  Every `export const` must be initialized. Example: `export const X = 1`.
* **Unsupported export forms**\
  Remove `export *`, `export { a as b }`, or destructured exports.
* **Variable exports with let or var**\
  Use `const` or `function` for exports.
* **Type level exports**\
  Keep the file runtime only. Remove `class`, `enum`, `interface`, and `type` exports.

### Best practices

* Keep modules small. Each Shared Code module should serve a single purpose.
* Validate inputs inside exported functions. Return safe defaults rather than throwing where possible.
* Avoid non-determinism unless you need it. For randomness, consider seeding or documenting expected behavior.
* Document each export at the top of the file with a brief comment.

### Troubleshooting

* **Import not found**\
  Verify the Shared Code module name matches the string in your `import` statement. Save your shared code after edits.
* **Validation error on save**\
  Compare your code against the Allowed and Disallowed lists above, then adjust.


# Add Javascript Libraries

Insert Javascript libraries into the head code of the form to use library functions within the logic rules

## Importing Libraries&#x20;

To import a library into your form:

1. Open the form settings and navigate to the **Form Properties** tab.
2. Scroll down until you see the **Custom Head Code** text area.
3. Insert a `<script>` tag pointing to the desired JavaScript library (e.g., via CDN).
4. Click **Save** to apply your changes.

<figure><img src="/files/YClphNjhtWCeEm8FFLjC" alt=""><figcaption></figcaption></figure>

## Using the Libraries in a Logic Rules

Once a library is imported, it can be accessed within a logic rule via the `window` object, like so: `window.LibraryName.method()`


# Reusable Logic Configs

Define globally available logic configurations to use in your Javascript logic rules

If you have variables or configuration that are too large or complex to define in your rules directly, you can leverage **configs** to define that information for your rules to access in an efficient way.

## Set up a Config

To set up a config, navigate to your form logic rules and click on the **Settings** button. This will open a pop-up that gives you an option to upload your config in CSV format. A typical workflow is:

1. Define your configuration in a spreadsheet (e.g. a list of stores by region, list of activities by time, etc). Make sure the first row of your CSV is a list of column headers.
2. Export the spreadsheet to a CSV and upload it to Feathery.
3. Feathery automatically stores and indexes your uploaded data, making it available to efficiently query from your rule itself. The data is formatted as an array of objects, where each object is a row of the spreadsheet that maps from a sepcific header to the value for that specific row and column.

<figure><img src="/files/gKNnX0vWEzQFTgoc3Enz" alt=""><figcaption></figcaption></figure>

## Use Configs in a Logic Rule

To use a config in your logic rule, you can leverage the `feathery.getConfig({filter, keys, unique})`  method in your rule.

* `filter`: A map from spreadsheet headers to the values that rows being returned must contain. If not specified, all config values will be returned.
* `keys` : A list of spreadsheet columns to be returned. If not specified, all columns will be returned.
* `unique` : Only return spreadsheet rows with unique sets of values to be returned.

Make sure to `await` the promise returned from the method to get the actual values to use.


# Examples

Examples of common problems you may solve with logic rules.

{% content-ref url="/pages/KRS1WO8AojdMrmHIlke3" %}
[API Connectors](/platform/build-forms/advanced-logic/examples/api-connectors)
{% endcontent-ref %}

{% content-ref url="/pages/sHHTfqRj1ieMIXGuosYc" %}
[Field Validation](/platform/build-forms/advanced-logic/examples/field-validation)
{% endcontent-ref %}

{% content-ref url="/pages/yFzMIzrIoeUGSPUfg52m" %}
[Initialize Date Field](/platform/build-forms/advanced-logic/examples/initialize-date-field)
{% endcontent-ref %}

{% content-ref url="/pages/4KT1I89pp5lA4b3AaWP3" %}
[Dynamically set field placeholder](/platform/build-forms/advanced-logic/examples/dynamically-set-field-placeholder)
{% endcontent-ref %}

{% content-ref url="/pages/5AoS5DDRWhtZ08iPfkwb" %}
[Randomize Field Option Order](/platform/build-forms/advanced-logic/examples/randomize-field-option-order)
{% endcontent-ref %}


# API Connectors

{% content-ref url="/pages/HaAFopn0He8Z8EdvMJFQ" %}
[Dynamic dropdown options](/platform/build-forms/advanced-logic/examples/api-connectors/dynamic-dropdown-options)
{% endcontent-ref %}

{% content-ref url="/pages/uXR983YvcQss7kZGtQzY" %}
[Dynamic form navigation](/platform/build-forms/advanced-logic/examples/api-connectors/dynamic-form-navigation)
{% endcontent-ref %}

{% content-ref url="/pages/pGHyc5XIq5BWUZTg8iBS" %}
[Pre-fill form from Salesforce](/platform/build-forms/advanced-logic/examples/api-connectors/pre-fill-form-from-salesforce)
{% endcontent-ref %}

{% content-ref url="/pages/bdmwWuFnC4OAPb0Zb87n" %}
[Pre-fill field options from Google Sheets](/platform/build-forms/advanced-logic/examples/api-connectors/pre-fill-field-options-from-google-sheets)
{% endcontent-ref %}

{% content-ref url="/pages/yW50NaYvb3Oi4jW9qsLa" %}
[Pre-fill form from Hubspot](/platform/build-forms/advanced-logic/examples/api-connectors/pre-fill-form-from-hubspot)
{% endcontent-ref %}


# Dynamic dropdown options

The example rule code below loads a dropdown field's options by calling a REST web service on the load of a step.  The returned data is used to load the options.

```
const response = await fetch('https://dog.ceo/api/breeds/list/all');
const dogs = await response.json();
// set the options to the keys of the returned object
DropdownDogs.options = Object.keys(dogs.message);
```


# Dynamic form navigation

The example code below calls a web service and then makes a step navigation decision based on the return data.  In this simplified example, the user's credit score is retrieved when a step is submitted and used to determine which step to navigate to next.

<pre><code>// When the user selects one of the refinance options, check their credit score and if > 680, goto premium offers step

function checkCreditScore() {
  // not a real url/service, example only
  // using ssn form field as url parameter
<strong>  return fetch(`https://www.example.com/checkCreditScore/?ssn=${ssn.value}`)
</strong>    .then((response) => response.json());
}

const refinanceOption = homeInterest.value;
if (['Cash out refinance', 'Refinancing'].includes(refinanceOption)) {
  const score = await checkCreditScore();
  if (score > 680) feathery.goToStep('premium-offers');
}

</code></pre>


# Pre-fill form from Salesforce

Use a user email to pull their Salesforce data and pre-fill form fields

## Overview

This rule runs after a user email is collected on `Step 1` and the step is submitted. The email is used to query a Salesforce instance to fetch other information about the user to prefill onto `Step 2`.

This pattern can be used to look up any Salesforce standard or custom objects based on any field or combination of fields (email, Salesforce ID, etc.)

## Rule Logic

```
// Get user's entered email
const email = salesforceEmail.value;

const query = `SELECT Id, Name, AccountId FROM Contact WHERE Email='${email}' LIMIT 1`;
const API_ENDPOINT = `https://feathery2-dev-ed.develop.my.salesforce.com/services/data/v50.0`;

// Issue request to Salesforce to fetch contact data
const data = await feathery.http.GET(`${API_ENDPOINT}/query?q=${encodeURIComponent(query)}`);
const record = data.records[0];

// Update Feathery fields with data pulled from Salesforce
salesforceName.value = record.Name;
salesforceAccount.value = record.AccountId;
```

## API Connector

Under the API connectors tab, create a connector with the following configuration:

* Base URL: <https://feathery2-dev-ed.develop.my.salesforce.com/services/data/v50.0>
* Allowed Endpoints: `/query`
* Headers:
  * `Authorization`: `Bearer {{salesforce_token}}`

Feathery will automatically detect and populate your Salesforce token in the header when you have Salesforce turned on in the `Integrations` tab.

## Demo

{% embed url="<https://www.loom.com/share/af79e50cb849472795598906efe27566?sid=7f9e11f0-a80d-4870-8080-39a7527a17bc>" %}


# Pre-fill form from Hubspot

Use a user email to pull their Hubspot data and pre-fill form fields

## Overview

This rule runs after a user email is collected on `Step 1` and the step is submitted. The email is used to query the Hubspot account to fetch other information about the user to prefill onto `Step 2`.

This pattern can be used to look up any Hubspot records based on any field or combination of fields (email, Salesforce ID, etc.)

## Rule Logic

```
// Get user's entered email
const email = hubspotEmail.value;

const API_ENDPOINT = `https://api.hubapi.com/contacts/v1/contact/email/${email}/profile`;

// Issue request to HubSpot to fetch contact data
const data = await feathery.http.GET(API_ENDPOINT);
const properties = data.properties;

// Update Feathery fields with data pulled from Hubspot
hubspotFirstName.value = properties.firstname.value;
hubspotLastName.value = properties.lastname.value;
```

## API Connector

Under the API connectors tab, create a connector with the following configuration:

* Base URL: <https://api.hubapi.com>
* Allowed Endpoints: `/contacts/v1/contact/email`
* Headers:
  * `Authorization`: `Bearer {{hubspot_token}}`

Fill in `hubspot_token` with your Hubspot `hapikey` or an access token.


# Pre-fill field options from Google Sheets

Pre-fill field options with dynamically pulled data from a Google Sheet

## Overview

This rule runs as the first step of the form is loaded. It queries a Google Sheet that has a **Class Name** header and several class names defined below, within the same column. The class names are then set as options of the `CoursesDropdown` field.

## Rule Logic

```
function fetchSheetData(spreadsheetId, sheetName, apiKey, callback) {
    // API endpoint URL
    const url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${sheetName}?key=${apiKey}`;

    // Fetch data from Google Sheets
    fetch(url)
        .then(response => response.json())
        .then(data => callback(data.values)); // Return the sheet values
}

function convertToJSON(data) {
    const [headers, ...rows] = data;
    return rows.map(row => {
        let obj = {};
        headers.forEach((header, index) => obj[header] = row[index]);
        return obj;
    });
}

// Usage
const API_KEY = 'DUMMY_KEY'; // Replace with your Google Sheets API Key
const SPREADSHEET_ID = 'DUMMY_ID'; // Replace with your spreadsheet ID
const SHEET_NAME = 'Sheet1'; // Replace with your sheet name

fetchSheetData(SPREADSHEET_ID, SHEET_NAME, API_KEY, (data) => {
    const jsonData = convertToJSON(data)
    const classes = jsonData.map(account => account["Class Name"])
    CoursesDropdown.options = classes
    
})
```


# Generate ChatGPT Message

Integrate your Feathery form with GPT or any AI service to generate intelligent responses to form inputs that are editable and can power logic.

## Overview

This rule runs after a location and time is collected on `Step 1` and the step is submitted. The field entries are used to query ChatGPT to generate a customized invite message that is editable in a [text area](/platform/build-forms/elements/fields/text-area) field on `Step 2`.

This pattern can be used to integrate with any AI service to generate any content from any user inputs.

## Rule Logic

```
const location = AILocation.value;
const time = AITime.value;
const eventType = AIEventType.value;

const res = await feathery.http.POST('https://api.openai.com/v1/chat/completions', {
    "model": "gpt-3.5-turbo",
    "messages": [
      {
        "role": "system",
        "content": "You are a friendly event planner. Maximum 50 word responses."
      },
      {
        "role": "user",
        "content": `Create an invitation message for an event. The location is ${location}. The time is ${time}. The event type is ${eventType}.`
      }
    ]
})

AIMessage.value = res.choices[0].message.content;
```

## API Connector

Under the API connectors tab, create a connector with the following configuration:

* Base URL: <https://api.openai.com/v1>
* Allowed Endpoints: `/chat/completions`
* Headers:
  * `Authorization`: `Bearer {{OPENAI_API_KEY}}`

Fill in `{{OPENAI_API_KEY}}` with your OpenAI API key.


# Field Validation

{% content-ref url="/pages/9rVVAn50XrBK1Tl6aHNK" %}
[Complex Field Validation](/platform/build-forms/advanced-logic/examples/field-validation/complex-field-validation)
{% endcontent-ref %}

{% content-ref url="/pages/VSWJuaiP0GQR26dimYOq" %}
[Date and Time Validations](/platform/build-forms/advanced-logic/examples/field-validation/date-and-time-validations)
{% endcontent-ref %}


# Complex Field Validation

Sometimes the standard custom field validation feature is not powerful and flexible enough to do more complex validations.  For example, if you wish to apply a validation to a field that requires the use of a regular expression, only the advanced logic rule feature supports this.

In the example below, the FullName field is required to have at least two parts (first and last names).  A regular expression is employed.

```
// Require the FullName field to have both first and last names
// Note that Feathery makes the field FullName available as a global variable

if (!FullName.value.match('[a-zA-Z]+ [a-zA-Z]+')) {
  FullName.setError('Please enter your full name');
}
```


# Date and Time Validations

Comparing dates in validation logic

## Overview

In these examples, the rules are comparing and validating date selector fields, both with and without the time component. &#x20;

These rules run on the submit event and general you will want to run validation logic on submit so that if the validation fails, the form will not submit.

## Comparing Dates

In this example, the logic ensures that the trip start date is on or after the current date and the end date is after the start.  The date selector fields are dates only (no time).

```
// Ensure trip start is today or later and trip end is after start

// get today's date
const today = new Date(new Date().toISOString().slice(0,10));

// make sure and trip-start-date is >= today
if (TripStartDate.value && TripEndDate.value) {
  const tripStartDate = new Date(TripStartDate.value);
  const tripEndDate = new Date(TripEndDate.value);
  
  if (tripStartDate.getTime() < today.getTime()) {
    // The date is earlier than today, so set a validation error on the trip-start-date field
    TripStartDate.setError('Start date must be on or after today');
  }
  if (tripEndDate.getTime() <= tripStartDate.getTime()) {
    // The end date is not after start, so set a validation error on the trip-end-date field
    TripEndDate.setError('End date must be on or after Start Date');
  }
}

```

## Comparing Dates with a Time part

In this example, the logic ensures that the help request **response** date and time is after the help request date and time.  The date selector fields are dates with a time part.

```
// Validate that the reponse time is after request time

// make sure the fields are filled before comparing
if (HelpRequestDateTime.value && HelpRequestResponseDateTime.value) {
  const requestTime = new Date(HelpRequestDateTime.value);
  const responseTime = new Date(HelpRequestResponseDateTime.value);
  
  if (responseTime.getTime() <= requestTime.getTime()) {
    // Response is not after request, so set a validation error on the help-request-response-date-time field
    HelpRequestResponseDateTime.setError('Response time must be after request time');
  }
}
```


# Initialize Date Field

Initialize Date and Date-Time fields

## Overview

In this example, the rule run on the load of a step and initializes both a date only field and a date+time field to the present date/time.

## Rule Logic

The below logic gets the current date and builds date and date-time values appropriate to initialize the fields.  The fields are only initialized if they do not already have a value set.

```
// Initialize a date and date/time to today and now respectively

// get today's date
const now = new Date();
const today = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')}`;

const nowInUtc = `${now.getUTCFullYear()}-${(now.getUTCMonth() + 1).toString().padStart(2, '0')}-${now.getUTCDate().toString().padStart(2, '0')}` + 
`T${now.getUTCHours().toString().padStart(2, '0')}:${now.getUTCMinutes().toString().padStart(2, '0')}:${now.getUTCSeconds().toString().padStart(2, '0')}Z`;

// only initialize the response date if not already set
if (!ResponseDate.value)
  ResponseDate.value = today;

// only initialize the event date and time if not already set - set it in UTC
if (!EventDateTime.value)
  EventDateTime.value = nowInUtc;
```




---

[Next Page](/llms-full.txt/1)

