---
schema: rendered-html/v1
title: "State-machine Rules | Developer Portal for YouTrack and Hub Documentation"
source_url: https://www.jetbrains.com/help/youtrack/devportal/state-machine-per-issue-type.html
generated_at: 2026-06-11T15:14:33.062600+00:00
---

Feedback

Thanks for your feedback!

Was this page helpful?

YesNo

Developer Portal for YouTrack and Hub

- Developer Portal
- Apps
- MCP Tools
- YouTrack Workflows
- JavaScript Workflow Quick Start Guide
- Create a Workflow
- Edit a Workflow
- Workflow Rule Types
- On-change Rules
- On-schedule Rules
- [State-machine Rules](state-machine-per-issue-type.html)
- Action Rules
- [SLA Rules](jetbrains__com__help__youtrack__devportal__sla-rules.md)
- Custom Scripts
- Transactions
- Use Cases for Workflows
- Web-based Workflow Editor
- Using an External Code Editor
- Default Workflows
- Custom Workflow Repository
- JavaScript Workflow Reference
- YouTrack Import Scripts
- YouTrack Form API
- YouTrack REST API
- Hub REST API

1. YouTrack Workflows
2. Workflow Rule Types
3. [State-machine Rules](#0)

# State-machine Rules﻿

Last modified: 07 May 2026

A state-machine rule regulates the transitions from one value to another for a custom field.

You can apply a state-machine rule to any enumerated custom field. However, the most common use case for a state-machine rule is to regulate transitions between values for the State field or another custom field that stores a `state` type.

When a state-machine rule is applied to a project, the options that are shown in the drop-down list for the field are restricted to the transitions that are defined in the state-machine rule for the current state. The action defined for each value in the state-machine rule is displayed in the drop-down list. If the actual value is different from the name assigned to the action, it is shown to the right of the action in parentheses. The following image illustrates how the list of allowed transitions for the State field changes based on the current value.

[![Statemachine field values](https://crawler.prototype.prolicht.digital/l/K3A0mO)](https://resources.jetbrains.com/help/img/youtrack/statemachine-field-values.png)
> ### warning
>
> The allowed transitions for each value are displayed even when you have defined a guard that potentially blocks the selection of the value. If the guard condition is not met, the selection is ignored.
>
> If you add guards to your transitions, be sure to use the `workflow.message` method to explain the restriction that is applied by the workflow.

YouTrack provides templates for two types of state-machine rules.

- A basic state-machine rule that regulates the transitions from one value to another for a custom field. To learn more about this type of state-machine rules, see [Basic State-machine Rules](/help/youtrack/devportal/state-machine-per-issue-type.html#sample-state-machine-rule).
- A state-machine rule that imposes separate sets of state transitions based on the current value in a specified field. To learn how to script this type of state-machine rules, see [State-machine Rules per Issue Type](/help/youtrack/devportal/state-machine-per-issue-type.html#state-machine-rule-per-issue-type).

  > ### note
  >
  > Availability
  >
  > The ability to create and edit state-machine rules per issue type is only supported by the JavaScript Editor. This feature is not available for the Workflow Constructor.

Both of these state-machine rule types are built using a similar collection of parameters.

## Basic State-machine Rules﻿

A basic state-machine rule regulates the transitions from one value to another for a custom field for all issues in any project that uses the workflow. For each transition, you can add one or more actions. Additional properties for each transition determine how these actions are applied.

- Transitions that use `onEnter` and `onExit` properties perform an action when the value for the field is set. These actions are executed by the user who changes the value of the field.
- Transitions that use the `after` property perform an action after the specified time frame. These actions are executed by the workflow user account.

This example that follows shows a simplified state-machine rule. This rule defines the transitions that are allowed for the State field.

The following transitions are defined in this rule:

- From the initial Open state, the value can change to In Progress. An additional transition with the name `reminder` includes an `after` property that reminds the project owner if an issue remains in the Open state for more than two days.
- When the issue transitions from Open to In Progress, the `onEnter` property for the `In progress` state automatically assigns the issue to the user who changes the value of the State field.
- From In progress, the value can change to Fixed or Open. When an issue transitions from In Progress back to Open, an automation for this transition asks the user to unassign the issue. Otherwise, the transition is blocked.
- The final state is Fixed. There are no additional transitions that are defined for this value. Once this value is set, it cannot be changed.

The following diagram shows an abstract representation of this rule.

![A diagram that illustrates the supported transitions between values for the State field.](https://crawler.prototype.prolicht.digital/l/bjw5Lq)

The workflow rule that supports this state machine is written as follows:

```
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const workflow = require('@jetbrains/youtrack-scripting-api/workflow');

exports.TWO_DAYS = 2 * 24 * 60 * 60 * 1000;
exports.rule = entities.Issue.stateMachine({
  title: 'Basic state-machine',
  fieldName: 'State',
  states: {
    Open: {
      initial: true,
      transitions: {
        start: {
          targetState: 'In progress'
        },
        reminder: {
          targetState: 'Open',
          after: exports.TWO_DAYS,
          action: (ctx) => {
            const issue = ctx.issue;
            issue.project.leader.notify('Reminder', 'Check out an issue ' + issue.id);
          }
        }
      }
    },
    'In progress': {
      onEnter: (ctx) => {
        ctx.issue.fields.Assignee = ctx.currentUser;
      },
      onExit: (ctx) => {
      },
      transitions: {
        fix: {
          targetState: 'Fixed'
        },
        reopen: {
          guard: (ctx) => {
            if (ctx.issue.fields.Assignee) {
              workflow.message('You cannot reopen an issue unless you unassign it first');
              return false;
            }
            return true;
          },
          targetState: 'Open'
        }
      }
    },
    Fixed: {
      transitions: {}
    }
  },
  requirements: {
    Assignee: {
      type: entities.User.fieldType
    }
  }
});
```

The components that define this state-machine rule are as follows:

- The `require` statements reference the `entities` module and the `workflow` module in the workflow API.
- The `exports.TWO_DAYS` property sets a local variable that is used to send the reminder for issues that have the Open status. This variable is assigned a value that equals 2 days in milliseconds.
- The `exports.rule` property uses the `Issue.stateMachine` method to export the script that follows the declaration as a state-machine rule.
- The body of the rule itself contains definitions for the following properties:

  | Property | Description |
  | --- | --- |
  | title | An optional human-readable title. |
  | fieldName | The name of the custom field that is managed by the state-machine rule. |
  | states | The list of field values and definitions for the transitions between them. Values that contain spaces and special characters are set in single quotes. |
  | requirements | The list of entities that are required for the rule to execute without errors. This property ensures that rules can be attached to projects safely.  In this example, we only require that there is an Assignee field that stores a `user` type in the projects to which the rule is attached. If this field is absent, an error is shown in the Workflows list. The rule cannot be enabled until the required field is attached.  For a state-machine rule, you do not need to add the field that is managed by the state machine to the requirements. This field and its values are derived from the `states` property. |
- Each of the values that are defined in the `states` property contains definitions for the following additional properties:

  | Property | Description |
  | --- | --- |
  | initial | A Boolean property that determines which of the values in the list is set when an issue is created. Exactly one value must set this property to `true`. For other values that are not set as the initial value, this property is optional and can be omitted. |
  | onEnter | An optional function declaration that is called when the corresponding value is assigned to an issue. These functions behave similar to actions in other types of rules. |
  | onExit | An optional function declaration that is called when the field value is changed from the current value to another value. These functions behave similar to actions in other types of rules. |
  | transitions | The list of possible target values that can be set for each value in the list.  In this example, we only require that there is an Assignee field that stores a `user` type in the projects to which the rule is attached. If this field is absent, an error is shown in the Workflows list. The rule cannot be enabled until the required field is attached.  For a state-machine rule, you do not need to list the values that are used by fields that are managed by the state machine in the requirements. This field and its values are derived from the `states` property. |
- Each of the values that are defined in the `transitions` property contains a `name` property. The name is used to set this value in a command and is also shown in the list of values for a custom field. Each transition name contains definitions for the following additional properties:

  | Property | Description |
  | --- | --- |
  | targetState | The actual name of the value that is set when the command specified in the `name` property is applied. |
  | after | An optional property that sets an interval for performing an action. The action itself is specified in the `action` property. |
  | action | An optional property for any transition that behaves similar to actions in other types of rules.. |
  | guard | An optional condition that determines when the transition is allowed. If the guard condition is not met, the value for the custom field cannot change to the value that is defined for this transition.  In this example, for an issue to transition from In progress back to an Open state, the issue must be unassigned. The guard contains a function that uses the `message` method from the `workflow` module to provide feedback to the user who attempts to reopen an issue without unassigning it first. |

## State-machine Rules per Issue Type﻿

> ### note
>
> Availability
>
> The ability to create and edit state-machine rules per issue type is only supported by the JavaScript Editor. This feature is not available for the Workflow Constructor.

A state-machine rule per issue type adds an additional dimension to a basic state machine. Instead of regulating a single set of transitions for values in a custom field, the rule provides separate state machines for each of the possible values in a separate field.

Just like you can apply a state-machine rule to any custom field, not just the default State field, you can make type-specific transitions depend upon the value in any enumerated field, not just the default Type field. The most common use case is to regulate transitions between values for the State field based on the current value for the issue Type.

A state-machine rule per issue type is written similarly to a basic state-machine rule, with the following changes:

- The `fieldName` property is replaced with an alias, `stateFieldName` in the template for a new state-machine rule. The value for this property determines which field is managed by the state-machine rule.
- A separate property for the `typeFieldName` lets you specify the field that dictates which state-machine rule applies to the state field. The presence of this property is the main difference between a basic state-machine rule (which doesn't use it) and a state-machine rule per issue type.
- The `states` property is replaced with an alias, `defaultMachine`. This property describes the default set of transitions for values in the managed field. This state-machine rule applies to issues that are not assigned a value for which an alternative state-machine rule has been defined.
- The `alternativeMachines` property stores a collection of state-machine transitions for designated values in the custom field that is set for the `typeFieldName` property.

When you specify a value for the `typeFieldName`, configure the `defaultMachine`, and define at least one state machine for a value in the `alternativeMachines` section of the rule, you end up with a state-machine rule per issue type. The field that you specify for the `typeFieldName` property functions as a switch between the default state-machine rule and the collection of alternative state-machine rules.

The following example shows how to build a state-machine rule per issue type. Each of the tabs below describes the state-machine rule that corresponds to one of the possible values for the Type field.

Task

Bug

Feature

![A diagram that shows the default transitions for the State field in a state-machine per issue type.](https://crawler.prototype.prolicht.digital/l/bjw5Lq)

First, we take the logic that is applied in the basic state-machine rule above and apply it as a default to any type of issue in a project. As a result, the values for the State field in issues that are assigned the Task type are regulated according to the model shown above. This state-machine rule also applies to issues that are assigned any type that isn't assigned an alternative state-machine rule.

![A diagram that shows the transitions for the State field in issues that are assigned the Bug type.](https://crawler.prototype.prolicht.digital/l/yHvZx7)

Next, we'll add an alternative state-machine rule just for issues that are assigned the Bug type.

- For this issue type, we make it possible to assign the value Can't Reproduce to the State field.
- We'll make it a little more flexible than the default state-machine rule, allowing a transition from Fixed back to Open. This means that we can reopen issues when a regression occurs instead of having to report the same problem more than once.
- We'll also make it possible to transition from Can't Reproduce back to Open.

We add some automation to specific states and transitions.

- When an issue moves from Open to In Progress, the issue is assigned to the user who changes the issue state. This is the same definition for the `onEnter` property that is used for the `In progress` state in the basic state machine.
- For the transition from In Progress to Can't Reproduce we add an `action`. This displays an alert that asks the user to describe what steps they took to try to reproduce the bug.
- From Fixed the issue can only transition back to the Open state. As a result, we can add an automation for the `onExit` property to the definition for the `Fixed` field. This automation sends a notification to the assignee to inform them that the issue was reopened.

![A diagram that shows the transitions for the State field in issues that are classified as feature requests](https://crawler.prototype.prolicht.digital/l/dXPDZT)

We'll add one more alternative state machine and apply this to issues that are assigned the Feature type.

- For these types of issues, we add the possibility to move an issue directly from an Open state to Rejected. If anybody suggests a feature that the team decides doesn't fit with their vision of the product, they can throw it out.
- Just to be open to the possibility for a change in direction, we'll allow issues to transition from Rejected back to Open.
- All of the other transitions are similar to the state machine for bugs.

To keep it simple, we haven't added any automations to this state machine. That doesn't mean that there aren't any opportunities to automate or enhance the process for this issue type. Possible enhancements include:

- Using an `after` event to the set of transitions for the Open state to tag issues that remain in this state for over six months. You can then review all of the tagged issues in your next backlog grooming session.
- Adding an `onEnter` event to the In Progress state, prompting the user to assign the feature to a specific milestone or pipeline in the roadmap.
- Adding an `action` event to the transition from Rejected to Open, prompting the user to justify why they would reopen the feature request for future consideration.

> ### note
>
> Transitions Between State-machines
>
> For a state-machine rule per issue type, the field that is referenced in the `typeFieldName` property works as a switch. Changing the value of this field determines which state machine is applied to the current issue.
>
> When an issue is currently assigned a value in the managed field that is not supported by the state machine for a target issue type, the value for the managed field is reset to the initial value that is configured for the new state machine.
>
> Following our example here, let's say that you have an issue that is currently classified as a feature request that was rejected by the development team. You later learn that the problem is in fact a bug and should be fixed. By changing the value of the Type field from Feature to Bug, the value for the State field automatically changes from Rejected to Open. This is because the state machine that is associated with the Bug type doesn't support transitions to the Rejected state.

We can take the sample code for the basic state-machine rule and quickly convert it into a state-machine rule per issue type with just a few changes.

- While it's not required, we have replaced the name of the `fieldName` property with its alias, `stateFieldName`. As with the sample for the basic state-machine rule, we're managing transitions between values for a field called `State`. We don't change the value for this property.
- We've added the property `typeFieldName` and specified `'Type'` as its value. This means that the Type field can now be used as a switch to determine whether to apply the default state machine or an alternative state machine that is assigned to one of the possible values for this field.
- We've applied another change just for cosmetic purposes, replacing the name of the `states` property with its alias, `defaultMachine`. We don't change the value for this property. Instead, we reuse the basic state-machine rule, which will apply to any issue that isn't assigned one of the types that are defined in the `alternativeMachines` section of the rule.
- We've added a new section for `alternativeMachines`. Here, we specify state machines for issues that are assigned the values Bug and Feature in the Type custom field.

Here's the modified state-machine rule:

```
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const workflow = require('@jetbrains/youtrack-scripting-api/workflow');

exports.TWO_DAYS = 2 * 24 * 60 * 60 * 1000;
exports.ONE_WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000;
exports.rule = entities.Issue.stateMachine({
  title: 'State-machine per Issue Type',
  stateFieldName: 'State',
  typeFieldName: 'Type',
  defaultMachine: {
    Open: {
      initial: true,
      transitions: {
        start: {
          targetState: 'In progress'
        },

        reminder: {
          targetState: 'Open',
          after: exports.TWO_DAYS,
          action: (ctx) => {
            const issue = ctx.issue;
            issue.project.leader.notify('Reminder', 'Check out an issue ' + issue.id);
          }
        }
      }
    },
    'In progress': {
      onEnter: (ctx) => {
        ctx.issue.fields.Assignee = ctx.currentUser;
      },
      onExit: (ctx) => {
      },
      transitions: {
        fix: {
          targetState: 'Fixed'
        },
        reopen: {
          guard: (ctx) => {
            if (ctx.issue.fields.Assignee) {
              workflow.message('You cannot reopen an issue unless you unassign it first');
              return false;
            }
            return true;
          },
          targetState: 'Open'
        }
      }
    },
    Fixed: {
      transitions: {}
    }
  },
  alternativeMachines: {
    Bug: {
      Open: {
        initial: true,
        transitions: {
          'in progress': {
            targetState: 'In Progress',
            action: (ctx) => {
              if (!ctx.issue.fields.Assignee) {
                ctx.issue.fields.Assignee = ctx.currentUser;
              }
            }
          },
          'can\'t reproduce': {
            targetState: 'Can\'t Reproduce'
          }
        }
      },
      'In Progress': {
        transitions: {
          'reopen': {
            targetState: 'Open'
          },
          'fix': {
            targetState: 'Fixed'
          },
          'can\'t reproduce': {
            targetState: 'Can\'t Reproduce'
          },
          'remind': {
            after: exports.ONE_WEEK_IN_MS,
            targetState: 'In Progress',
            action: (ctx) => {
              if (ctx.issue.fields.Assignee) {
                const subj = '[YouTrack] Issue is in progress';
                const body = 'Issue ' + ctx.issue.id + ' is in progress for a week.';
                ctx.issue.fields.Assignee.notify(subj, body);
              }
            }
          }
        }
      },
      Fixed: {
        transitions: {
          'reopen': {
            targetState: 'Open'
          }
        },
        onExit: (ctx) => {
          if (ctx.issue.fields.Assignee) {
            const subj = '[YouTrack] Issue is reopened';
            const body = 'Issue ' + ctx.issue.id + ' requires your attention.';
            ctx.issue.fields.Assignee.notify(subj, body);
          }
        }
      },
      'Can\'t Reproduce': {
        onEnter: (ctx) => {
          workflow.message('Please leave a comment explaining what you tried to do.');
        },
        transitions: {
          'reopen': {
            targetState: 'Open'
          }
        }
      }
    },
    Feature: {
      Open: {
        initial: true,
        transitions: {
          'in progress': {
            targetState: 'In Progress',
          },
          reject: {
            targetState: 'Rejected'
          }
        }
      },
      'In Progress': {
        transitions: {
          reopen: {
            targetState: 'Open'
          },
          fix: {
            targetState: 'Fixed'
          }
        }
      },
      Fixed: {
        transitions: {
          reopen: {
            targetState: 'Open'
          }
        }
      },
      Rejected: {
        transitions: {
          reopen: {
            targetState: 'Open'
          }
        }
      }
    }
  },
  requirements: {
    Assignee: {
      type: entities.User.fieldType
    }
  }
});
```

The components that define this state-machine rule are as follows:

- The `require` statements reference the `entities` and `workflow` modules in the workflow API.
- The `exports.TWO_DAYS` property sets a local variable that is used to send the reminder for issues that have the Open status. This variable is assigned a value that equals 2 days in milliseconds.
- The `exports.ONE_WEEK_IN_MS` property sets a local variable that is used to send the reminder for bugs that have been In Progress for more than one week. This variable is assigned a value that equals one week in milliseconds.
- The `exports.rule` property uses the `Issue.stateMachine` method to export the script that follows the declaration as a state-machine rule.
- The body of the rule itself contains definitions for the following properties:

  | Property | Description |
  | --- | --- |
  | title | An optional human-readable title. |
  | stateFieldName | The name of the custom field that is managed by the state-machine rule. |
  | typeFieldName | The name of the custom field that regulates which state machine is applied to the managed field. |
  | defaultMachine | The list of field values and definitions for the transitions between them for the default state machine. The default state machine applies to any issue that isn't assigned one of the types that are defined in the `alternativeMachines` section of the rule.  Values that contain spaces and special characters are set in single quotes. |
  | alternativeMachines | Stores a collection of state-machine transitions for designated values in the custom field that is set for the `typeFieldName` property. |
  | requirements | The list of entities that are required for the rule to execute without errors. This property ensures that rules can be attached to projects safely.  In this example, we only require that there is an Assignee field that stores a `user` type in the projects to which the rule is attached. If this field is absent, an error is shown in the Workflows list. The rule cannot be enabled until the required field is attached.  For state-machine rules by issue type, you don't need to add any of the fields that are managed by the state machine to the requirements. This field and its values are derived from the `defaultMachine` property and the collection of fields that are defined for each value in the `alternativeMachines` section of the rule. The field that is set for the `typeFieldName` property is also implicitly added to the requirements for the workflow rule. |
- Each of the values that are defined in the `defaultMachine` property and each of the fields that are defined for each value in the `alternativeMachines` section of the rule contains definitions for the following additional properties:

  | Property | Description |
  | --- | --- |
  | initial | A Boolean property that determines which of the values in the list is set when an issue is created. Exactly one value must set this property to `true`. For other values that are not set as the initial value, this property is optional and can be omitted. |
  | onEnter | An optional function declaration that is called when the corresponding value is assigned to an issue. These functions behave similar to actions in other types of rules. |
  | onExit | An optional function declaration that is called when the field value is changed from the current value to another value. These functions behave similar to actions in other types of rules. |
  | transitions | The list of possible target values that can be set for each value in the list.  In this example, we only require that there is an Assignee field that stores a `user` type in the projects to which the rule is attached. If this field is absent, an error is shown in the Workflows list. The rule cannot be enabled until the required field is attached.  For a state-machine rule, you do not need to list the values that are used by fields that are managed by the state machine in the requirements. This field and its values are derived from the `states` property. |
- Each of the values that are defined in the `transitions` property contains a `name` property. The name is used to set this value in a command and is also shown in the list of values for a custom field. Each transition name contains definitions for the following additional properties:

  | Property | Description |
  | --- | --- |
  | targetState | The actual name of the value that is set when the command specified in the `name` property is applied. |
  | after | An optional property that sets an interval for performing an action. The action itself is specified in the `action` property. |
  | action | An optional property for any transition that behaves similar to actions in other types of rules.. |
  | guard | An optional condition that determines when the transition is allowed. If the guard condition is not met, the value for the custom field cannot change to the value that is defined for this transition.  In this example, for an issue to transition from In progress back to an Open state, the issue must be unassigned. The guard contains a function that uses the `message` method from the `workflow` module to provide feedback to the user who attempts to reopen an issue without unassigning it first. |
- The `alternativeMachines` property stores a set of values from the field that is referenced in the `typeFieldName` property. Each of the values that are assigned dedicated state machines is referenced by name.

- [State-machine Rules](/help/youtrack/devportal/state-machine-per-issue-type.html)
- [Basic State-machine Rules](/help/youtrack/devportal/state-machine-per-issue-type.html#sample-state-machine-rule)
- [State-machine Rules per Issue Type](/help/youtrack/devportal/state-machine-per-issue-type.html#state-machine-rule-per-issue-type)

On-schedule RulesAction Rules

- Privacy & Security
- Terms Of Use
- Trademarks
- Legal

Copyright © 2000–2026 JetBrains s.r.o.

![](https://bat.bing.com/action/0?ti=5220035&tm=gtm002&Ver=2&mid=45639b15-e67d-4127-aef4-d779032cdb73&bo=4&sid=452929b065a111f19a7fe7356f74a419&vid=aa3122f0648711f1a10d6d095e10fd87&vids=0&msclkid=N&pi=918639831&lg=en-US&sw=1920&sh=1080&sc=24&tl=State-machine%20Rules%20%7C%20Developer%20Portal%20for%20YouTrack%20and%20Hub%20Documentation&p=https%3A%2F%2Fwww.jetbrains.com%2Fhelp%2Fyoutrack%2Fdevportal%2Fstate-machine-per-issue-type.html&r=&lt=1094&pt=1781190868948,34,34,,,2,2,2,2,2,,3,30,30,39,423,424,424,1093,1094,1094&pn=0,0&evt=pageLoad&sv=2&asc=G&cdb=AQAS&rn=5754)
