Custom fields

In addition to our built-in field types such as items, text, or column types, you can now build your very own!

The “Field Type” editor allows you to create and reuse your configuration fields in your integration recipes. An example of this could be a project stored in a different platform, or even a list of all your monday.com boards.

What is a custom field?

The benefit in using a custom field is that it allows you to create a reusable object for your integration recipes (i.e. you can use them in both your trigger and action blocks).

Custom fields are useful when you want to load configuration data from an external platform into your monday.com integration recipe for use. For example, you can load options for your app users to choose from when configuring the recipe (i.e. a list of Zendesk tickets or Slack channels).

It’s important to consider reusability when building custom fields. This can help you in building out multiple integration recipes in your app, all using the same custom fields you define.

Custom Field Type Editor Configuration

To create a new custom field, click into the “Field Types” tab of your app's integration recipe:

1000

From there, you can click “Create new”.

The “Name” of your custom field should describe what your custom field is (i.e. “Zendesk Ticket Fields,” or “monday.com Boards List.”). Adding a short “Description” will also help explain what your custom field contains.

Set a “Default field key” that will act as the name of this custom field type when you use it in your integration recipes.

The automation configuration of your custom field can be one of three options.

Text Field Type

The ”Text” type allows you to create a field type with a predefined field key for repeated use (for example, if you have a project with the field key “projectId,” you can utilize this key for all other trigger and action blocks instead of re-configuring it each time).

Text columns allow you to select whether your text custom field type has numbers only. This will force users to only add numeric characters, which can be useful if the custom field will only include numerals, like ID numbers.

List Field Type

The ”List” type allows you to generate an array of options for selection (for example, if you want to pull up a list of all the Zendesk tickets in an account).

If you want to manually define only a few field options, you can type different key-value pair “Options” in your list field type.

Alternatively, if you only want to load all of the list options remotely, you can populate the “Remote Options URL” with an endpoint URL that will contain all the array of values in your list.

![custom field URL] (https://dapulse-res.cloudinary.com/image/upload/v1614370131/remote_mondaycom_static/uploads/HelenLu/Custom%20Fields/field_URL.png)

The data returned by your remote options URL should be an array of key-value pairs. You can use pagination to go trough your remote options. Each option should have a title and value, like so:

app.post("/fetchRemoteOptions", function(req, res) {
    const { payload } = req.body;
    const { pageRequestData } = payload;
    const { page = 1 } = pageRequestData;

    res.status(200).send({
        options: [
          {title: "Label 1", value: "label_1"},
          {title: "Label 2", value: "label_2"}],
        isPaginated: true,
        nextPageRequestData: { page: page + 1 }
    ]);
})

Regarding pagination, when at the last page, you should either return isLastPage: true or not return nextPageRequestData at all. You may also have the possibility to add a limit parameter, depending on the third party software's support for it.

You can also add “Dependencies” to the list field type that determine what other object this list field type is dependent on. For example, if I want to load all of the items on a specific monday.com board, the list field type of the item custom field will include a dependency of the board custom field.

If you choose to utilize a dependency, the payload to your remote options URL will look like this:

{ 
    "payload": {
        "boardId": 12345678, 
        "automationId": 123456, 
        "dependencyData": {
             "boardId": 12345678 
         },
        "recipeId": 123456, //unique ID of the recipe in your app. the same recipe ID will be sent if two different accounts are using the same recipe
        "integrationId": 123456 //unique ID of the integration recipe on your board
    }
}

In the above example, the dependent custom field URL payload will contain the ID of the custom field it's dependent on (i.e. the POST request for the remote options URL that loads a list of items will contain the board Id on which the items are located).

Pagination for remote options

If your app returns a large amount of remote options, you can use pagination to return the options in smaller batches.

If the API you are fetching your options from limits the records that can be retrieved with a single request, you can use pagination to go through all of the items until the response is empty and all the items had been retrieved.

You will receive from Monday a payload when someone clicks on the field for getting your options that will be similar to this:

{
  recipeId: 30019598123,
  integrationId: 72966189123,
  automationId: 72966189123,
  pageRequestData: {},
  dependencyData: null
}

The payload contains a pageRequestData parameter, which indicates the page used for your request.

The response must contain your options (as an array of key-value pairs where each option should have a title and value), the parameter isPaginated (boolean), and nextPageRequestData which contains metadata for the following page. If the user requests more options, the value of nextPageRequestData will be sent in the next request as pageRequestData.

Here are two implementations:

app.post("/fetchRemoteOptions", async function(req, res) {
  // de-structure request payload
  const pageLimit = 10;
  const { payload } = req.body;
  const { pageRequestData } = payload;
  const { page = 1 } = pageRequestData;

  // call external API
  const response = await fetch(`https://yourApi.com/users?page=${page}&limit=${pageLimit}`);
  const myUsers = await response.json();

  // generate array of options
  const options =  myUsers.map((user) => {
    const value = {
      title: user.name,
      value: user.id
    }
    return { value, title: user.name };
  });
		  
  const nextPageRequestData = options.length === 0 ? null : { page: page + 1};
	
  // return HTTP response
  res.status(200).send({
    options, 			
    isPaginated: true,
    nextPageRequestData
  });
})
const getRepositories = async (token, payload) => {
  const octokit = new Octokit({ auth: token });
  const { pageRequestData } = payload;
  const { page = 1 } = pageRequestData;
  const reposResponse = await octokit.repos.list({per_page: YOUR_MAGIC_NUMBER, page });
  const repos = reposResponse ? reposResponse.data : [];
  const options = repos.map((repo) => {
    const value = {
      uniqueId: repo.id,
      owner: repo.owner.login,
      name: repo.name,
      full_name: repo.full_name,
      ownerType: repo.owner.type,
    };
    return { value, title: repo.name };
  });
  const nextPageRequestData = !repos.length ? null : { page: page + 1 };
  return { options, nextPageRequestData, isPaginated: true };
};

app.post("/fetchRemoteOptions", async function(req, res) {
  const { userId } = req.session;
  const { payload } = req.body;
  const token = await TokenService.getToken(userId);
  const response = await getRepositories(token, payload);
  res.status(200).send(response);
})

In the Generic example, we start on page 1 and get batches of 10 items each. The requests will continue until there are no more items to retrieve. The users' names would be the visible options to choose from. In this example, pageRequestData starts with a value of one and will change with every new request, adding one to the page number each time. When the response for the options is empty, nextPageRequestData is set to null and no more requests will be made afterwards.

In the Github example, we get all the repositories from Github (that had a default limit of 25 repositories for one request). The result will be that all our repositories will be available as options for our custom field.

The parameters you will use when fetching your options using an external API, may vary depending on how that API works.

Dynamic Mapping Field Type

The ”Dynamic Mapping” type will allow you to define a custom object that contains a set of subfields. This is useful if you want to load data from a third-party software (for example mapping Zendesk ticket information into columns on a monday.com board).

The “Field Definitions URL” will load a payload of data that contains all of the fields for your custom field. This can be useful when you want to map data between monday.com and a different software.

📘

TIP

Check out our Item Mapping and Custom Entities article for more information on how this custom field works.

Input and Output Fields

Each integration recipe has a “Trigger” and an “Action” block. The trigger block determines what initiates (or sets off) your integration recipe -- for example “When a Status changes”. The action block determines the action that you’re hoping to accomplish (for example “create a new item”).

Input and output fields are how the user passes configuration to a trigger/action block, and how data is transferred between blocks. For example, a user’s recipe configuration can be one of the input fields in your blocks, and the output fields of a trigger can be mapped to the input fields of the action.

Trigger I/O Fields

When building your own custom trigger, you can configure the input and output fields yourself.

The input fields of the trigger can come from either the context of your integration recipe, or from your recipe sentence (i.e. what the user chooses when using your integration recipe).

The output fields of your trigger are what will be mapped into your action’s input fields. If you’re using a built-in trigger, you can learn more about that trigger’s output fields by hovering your mouse over the toolkit information icon.

If you’re using a custom trigger, you can configure the output fields yourself. The output fields should contain the data you will be needing to utilize in your action block.

📘

TIP

Feel free to check out our Custom Triggers article for specifics on setting this up.

For example, let’s say you’re looking to create a monday.com item each time you receive a ticket in Zendesk, the output fields of your trigger (which is “When a new ticket is submitted in Zendesk,”) will need to include the board Id of your monday.com board where this recipe sits so that your new item can be created in this board.

Action I/O Fields

Your action block will include only the ability to customize input fields, not output fields. This is because the output of this block is the action itself.

📘

TIP

Feel free to check out our Custom Actions article for specifics on setting this up.

The input fields for your trigger will need to include all of the data needed to complete your action.

If you’re planning on using dynamic mapping into a monday.com item, the input field of your mapped item will need to be the output field of your trigger block and not the custom entity you’ve created. This is because the monday apps framework will do all of the mapping for you!

Remote Options Dependencies

In the example above, the “parent” custom field is the custom list of boards within a monday.com account. The dependent “child” custom field is the list of items on the selected board.

When setting up the custom action in your integration recipe that will utilize these remote options dependencies, the input field settings for the “child” (or the dependent custom field) will include a dependency on the “parent” custom field.

Your configuration should look like this:

1802

Congratulations Builder!

You now know the ins and outs of how data is transferred to, and between, the blocks in your integration recipes.

As a next step, check out the following resources:

📘

Join our developer community!

We've created a community specifically for our devs where you can search through previous topics to find solutions, ask new questions, hear about new features and updates, and learn tips and tricks from other devs. Come join in on the fun! 😎