The Adaptavist Group LogoDocumentation

Dynamic Forms

Add configurable string, number, and boolean inputs to workflow and automation scripts so one script can serve many rules without code changes.

With dynamic forms, you can add input fields to your scripts that appear when someone configures a scripted workflow action, a scripted workflow validator, or an automation action. This means a single script can be reused across multiple rules, with different values set for each rule, without any code changes.

Why are dynamic forms useful?

Scripts are often copied and pasted with small changes for different use cases, creating a maintenance burden over time. Dynamic forms reduce this by letting you create flexible scripts with annotated parameters, so the same script can serve multiple use cases without duplication. Admins can set those values directly when configuring their automation rules or workflows, and no ScriptRunner access or code knowledge is required.

In automation actions, string parameters also accept Jira smart values such as {{issue.key}}. This means the script can act dynamically on the triggering issue without the admin needing to hardcode anything. The right values are passed in automatically at runtime.

Each parameter stores a value that the script can use at runtime. The fields are generated from a Parameters interface at the top of the script using JSDoc annotations (tags) that control the label, description, default value, and validation.

Supported types

Each parameter you define in your script appears as an input field in the configuration UI. The type you give a parameter in your code determines what kind of input field is shown. Three types are supported:

  • String: a text field
  • Number: a number field
  • Boolean: a toggle (true/false switch)

If a field is defined with any other type, it will default to a text field.

Where dynamic forms are available

Dynamic forms are available only in scripts that can be configured by another admin in a workflow or automation rule. Other script types run automatically or are not user-configured, so they do not support dynamic forms.

Script typeDynamic forms supported
Script console
Event listeners
Scripted fields
Work item pickers
Workflow actions (scripted)
Workflow validators (scripted)
Scheduled jobs
Field behaviours
HTTP endpoints
Automation actions

How to define the fields

To create input fields, you define them inside an interface called Parameters at the top of your script. This is the list of values you want someone to be able to configure when using the script. This interface must be marked as exported; if it is missing or not exported, no fields will appear in the configuration UI.

Each field can be marked as required or optional:

  • A field without a ? marker is required.
  • A field with a ? marker is optional.

Tags available on each parameter type

Each field can include annotations to control its appearance and behaviour:

TagStringNumberBoolean
@label: custom field label
@description: help text below the field
@default: default value if none is provided
@minLength: minimum text length
@maxLength: maximum text length
@pattern: regex the text must match*
@min / @minimum: minimum number value
@max / @maximum: maximum number value

* @pattern is parsed and stored, but is not currently used to validate input.

Note: If no @label is given, a label is automatically generated from the field name (for example sendNotification becomes "send Notification").

How defaults work

When a field has a @default value and no value has been saved for it, the default is automatically applied at runtime. In the UI, each field shows its default value when it is first displayed (an empty string for text fields, an empty value for number fields, and off for boolean fields if no default is set).

When a script is deployed, the field definitions are automatically extracted from the code and stored.

Examples

The following examples show how dynamic forms work across different script types. Each example includes a Parameters interface with annotated parameters, so you can see how the tags translate into configurable fields in the UI. Use these as a starting point for your own scripts.

Workflow action example (string fields)

This example shows a workflow action with three configurable parameters: a string field for the issue key, a string field for the resolution comment, and a boolean toggle to notify the assignee. The script logs each parameter value to the console. In a real implementation, you might use these values to transition an issue, post a comment, or send a notification. The parameters would work the same way regardless of what the script does with them. It also demonstrates text validation tags (@pattern, @minLength, and @maxLength) to restrict what values can be entered.

import type { WorkflowPostFunctionEvent } from "@scriptrunnerhq/types/workflow";
 
export interface Parameters {
  /** @label Issue key
   *  @description The Jira issue key to transition
   *  @default TST-123
   *  @pattern ^[A-Z]+-\d+$
   *  @minLength 3
   *  @maxLength 20 */
  issueKey: string
 
  /** @label Resolution
   *  @description Resolution comment for the transition
   *  @default Done */
  resolution: string
 
  /** @label Notify assignee
   *  @description Whether to send a notification to the assignee
   *  @default true */
  notifyAssignee: boolean
}
 
export default function (
  payload: WorkflowPostFunctionEvent,
  params: Parameters,
) {
  console.log(`Transitioning ${params.issueKey}`)
  console.log(`Resolution: ${params.resolution}`)
  console.log(`Notify: ${params.notifyAssignee}`)
}

Workflow validator example (number fields)

This example shows a workflow validator that checks whether an issue meets certain conditions before allowing a transition. It exposes parameters for a story-point limit, a minimum number of approvers, and a list of labels that block the transition. Note that minApprovers is marked as optional with ?, meaning the check is skipped entirely if no value is provided.

import type { WorkflowValidatorEvent } from "@scriptrunnerhq/types/workflow";
import * as Issues from '@scriptrunnerhq/hapi/issues'
 
export interface Parameters {
  /** @label Story points
   *  @description Maximum story points allowed for this transition
   *  @default 5
   *  @minimum 1
   *  @maximum 20 */
  maxStoryPoints: number
 
  /** @label Required approvers
   *  @description Minimum number of approvers required
   *  @default 2
   *  @min 1
   *  @max 10 */
  minApprovers?: number
 
  /** @label Blocking labels
   *  @description Comma-separated labels that block the transition
   *  @default blocked,on-hold */
  blockingLabels: string
}
 
export default async function (
  payload: WorkflowValidatorEvent,
  params: Parameters,
) {
  const issue = await Issues.getByKey(payload.issue.key, {
    fields: ['StoryPoints', 'Approvers', 'labels'],
  })
 
  const { maxStoryPoints, minApprovers, blockingLabels } = params
  const storyPoints = issue?.fields?.StoryPoints ?? 0
  const approvers = issue?.fields?.Approvers?.length ?? 0
  const labels = issue?.fields?.labels ?? []
  const errors: string[] = []
 
  const blockedByLabel = labels.some((label: string) =>
    blockingLabels.split(',').map((l) => l.trim()).includes(label),
  )
 
  if (blockedByLabel) {
    errors.push(`Issue has a blocking label (${blockingLabels})`)
  }
 
  if (storyPoints > maxStoryPoints) {
    errors.push(`Story points (${storyPoints}) exceed maximum (${maxStoryPoints})`)
  }
 
  if (minApprovers && approvers < minApprovers) {
    errors.push(`Need at least ${minApprovers} approver(s), got ${approvers}`)
  }
 
  if (errors.length > 0) {
    return { result: false, errorMessage: errors.join('. ') }
  }
 
  return { result: true }
}

Automation action (boolean field)

This example demonstrates all three parameter types: a boolean toggle, a number field, and an optional string field. The script logs each parameter value to the console. In a real implementation, you might use these values to send a notification, filter issues by priority, or append a custom message. The parameters would work the same way regardless of what the script does with them.

export interface Parameters {
  /** @label Send email
   *  @description Whether to send an email notification
   *  @default true */
  sendEmail: boolean
 
  /** @label Priority threshold
   *  @description Only act on issues at or above this priority (1=highest)
   *  @default 3
   *  @minimum 1
   *  @maximum 5 */
  priorityThreshold: number
 
  /** @label Custom message
   *  @description Optional message to append to the notification */
  customMessage?: string
}
 
export default function (
  params: Parameters
) {
  if (!params.sendEmail) {
    console.log('Email notification skipped')
    return
  }
 
  console.log(`Filtering issues at priority ${params.priorityThreshold} or higher`)
 
  if (params.customMessage) {
    console.log(`Custom message: ${params.customMessage}`)
  }
}

When are errors shown?

Field validation errors are surfaced at different points depending on what you are doing:

  • When testing a script: If any field values are invalid (e.g., a required field is empty, a number is out of range, or text is too short), the test run is blocked until the errors are fixed.
  • When deploying a script: Field errors do not block deployment. Only other errors (like code compilation failures) will block it.
  • On workflow config pages: Saving is blocked if field validation errors exist or required fields are missing values.
  • On automation config pages: The "Update" button is disabled when field validation errors exist.

Not yet implemented

Fail-fast on field changes without defaults

When fields are added, removed, or their type changes, a warning banner is shown, but it does not block save, deploy, or execution.

Warning to add a default when creating new fields

No reminder is shown to encourage users to provide a default value for new fields. The warning banner highlights only the newly added required fields.

Search documentation

Start typing to search the docs.