# Triggers

> The five ways an automation workflow can start — status changes, new tickets, chat messages, packages linked after creation, and a command your team runs by hand — and the data each one publishes.

Source: https://joely.io/documentation/automations/triggers
Last updated: 2026-10-09

Every workflow starts with a **trigger node**. It names the ticket event the workflow listens to, and publishes that ticket's data to the nodes after it.

Most triggers have **no configuration of their own**. They pass the ticket's data — status, priority, tags, linked packages, assignees, and the form button the ticket was opened from — to the nodes that follow; [condition nodes](https://joely.io/documentation/automations/conditions.md) do all the filtering. The one exception is **Command**, which declares the commands it answers to — see below. Every trigger also publishes who performed the action: the **Actor** and their **Actor role** in your project (see [who performed the action](https://joely.io/documentation/automations/conditions.md#who-performed-the-action)).

## The five triggers

| Trigger | Fires when | Publishes, on top of the ticket data |
|---------|-----------|--------------------------------------|
| **Status changed** | A ticket moves from one status to another | Previous status, New status |
| **Ticket creation** | A new ticket is opened | Ticket source |
| **On chat message** | A chat message is posted in a ticket, by the customer, the team or the AI | Message author type, Message content, Message author (team member), Message ID |
| **Package selected** | A package is linked to a ticket after it was created | Selected packages, Selected package names |
| **Command** | A team member runs one of the workflow's commands from the ticket composer | Command name, and one field per declared argument |

## Status changed

Fires whenever a ticket moves from one status to another. Both ends of the transition are published, so a condition can test where the ticket came **from**, where it went **to**, or both.

One case does not fire it: when a customer requests ticket closure from the portal, that transition does not trigger workflows. The same status reached through a staff status change does.

## Ticket creation

Fires whenever a new ticket is opened — from the portal, the dashboard, or the API — including the package it was opened about. The ticket's first message belongs to this trigger, not to *On chat message*.

## On chat message

Fires whenever a chat message is posted in a ticket: by a customer, a member of your team, the AI assistant, or a workflow.

- The ticket's **first message** counts as *Ticket creation*, not as a chat message.
- **System event lines** (status change, assignment, package update…) never fire it.
- To react to one kind of author only, add a condition on **Message author type** (see [Conditions](https://joely.io/documentation/automations/conditions.md)). An AI reply is **AI assistant** and a message posted by a workflow is **Automation**, neither is *Team member*, so a workflow filtered on team members never reacts to the assistant or to another workflow. When replies need approval, the AI reply fires it once approved, which is when the customer receives it.
- A message an automation posts in the chat fires it too; to tell it apart from a person's reply, add a condition on **Message author (team member)** *is empty*.

## Package selected

Fires when a package is linked to a ticket **after** it was created:

- the customer answering the AI package picker,
- a team member setting it from the ticket,
- or a [**Set package**](https://joely.io/documentation/automations/actions.md#set-package) action.

A package chosen at creation belongs to *Ticket creation* instead, so the two never fire for the same choice; unlinking a package fires neither. The trigger publishes the packages that were just linked (**Selected packages** and **Selected package names**), next to the ticket's full list.

## Command

The only trigger a **person** starts. Instead of waiting for something to happen to the ticket, a team member types `/escalate` in the reply box and the workflow runs on that ticket.

The command lives **on the trigger node**, not on the Commands page: a workflow declares the commands that start it, so there is a single place to change them. The Commands page lists them next to the built-in and custom commands, with a switch that turns their **whole workflow** on or off (the same switch as on the Automations page, so it needs the Create, update, and delete automation workflows permission, plus Create and edit workflows shared with the whole project for a shared workflow), and can start one: **Create command → Run a workflow** opens a new workflow with a Command trigger already placed and selected, and its back arrow returns to the Commands page (it needs the Create, update, and delete automation workflows permission). A Command trigger declares **one** command. Each command has a name, a short description shown in the composer's autocomplete, and up to five **arguments**.

An argument is either **text** or a **number**, and is required or optional. It can also carry an optional description of up to 100 characters: it is shown under the command in the composer's autocomplete, and under the argument's name when you pick it in a condition. Arguments are matched **by position**, in the order you declared them, so every required argument must come before the optional ones. The last text argument takes the rest of the line — that is what lets a command end with a sentence:

```
/escalate billing the customer was charged twice
```

with `code` (text) then `reason` (text) declared, gives `code = billing` and `reason = the customer was charged twice`. Wrap a value in double quotes when it contains spaces but is not the last argument. A number argument can declare a minimum and a maximum; a value outside them is refused before the workflow runs, so no workflow has to re-check it.

Each argument becomes a field the nodes after it can read and interpolate, under its name: an argument named `reason` is `{trigger.command.args.reason}`. Argument values are treated as personal data: like message content, they are **never kept in the run history**.

Four things are worth knowing:

- **The command is never posted in the ticket.** If it were, it would fire *On chat message* and show `/escalate billing` to the customer. What the customer sees is whatever the workflow chooses to publish.
- **Command names are unique across the project.** A command cannot take the name of a built-in (`/close`, `/assign`, `/ai`…) or of one of the project's custom commands; saving a workflow that tries to is refused. It works both ways: a custom text command cannot be created or renamed to a name any workflow of the project declares, including drafts and disabled workflows.
- **Only runnable workflows offer their commands.** A workflow that is disabled, still a draft, or locked by your plan's workflow limit does not appear in the composer. The Commands page still lists the commands of a disabled workflow, greyed out with its switch off.
- **You see what the workflow did.** The command answers once its workflow has run. If an action was skipped or failed, the notification says which one and why, in the same words as the run history: for example, an **Ask AI** action skipped because an assistant draft is still awaiting approval, or failed because the monthly AI response limit is reached.

Any member who can reply to a ticket can run the command of a **shared** workflow. Note that the workflow then acts with **its own** authority, not the member's: a command can do things the person running it could not do by hand.

### Private command workflows

Like a custom command, a workflow can be **shared with everyone in the project** or kept for **you only**. Pick it in the selector next to the workflow's name, in the editor. The selector only appears when both are possible: without Create and edit workflows shared with the whole project a workflow you create is private, and a workflow holding a ticket trigger stays shared. A private workflow shows a **Private** badge instead.

- **A private workflow is yours alone.** Only you see it on the Automations and Commands pages, only you get its command in the reply box, and only you can edit, switch or delete it. Another member typing its name gets "command not found".
- **It can only start from commands.** A private workflow cannot hold a *Ticket creation*, *Status change*, *On chat message* or *Package selected* trigger: those would act on the whole team's tickets while nobody else can see the workflow. The editor refuses to add one to a private workflow, and does not offer **Only me** while one is on the canvas.
- **The project owner can still read it.** The owner sees every private workflow, with its creator's name, and can open it and its run history, but cannot change it.
- **What it does stays visible.** Privacy covers the workflow and its command, not its effects: a message it posts or a status it changes shows on the ticket as usual.
- **Sharing needs a permission.** Any member with Create, update, and delete automation workflows can create private workflows. Creating, editing, switching or deleting a **shared** one, and moving a workflow between shared and private, also needs Create and edit workflows shared with the whole project. Making a shared workflow private makes it **yours**, whoever created it.
- **It leaves with you.** Your private workflows are deleted when you leave the project or are removed from it, and when your account is deleted. Shared workflows you created stay with the team.

Private workflows count toward the project's monthly run quota and its workflow limit like any other. Command names stay unique across the whole project, private ones included.

## Several triggers in one workflow

A workflow can hold **more than one trigger**, each connected to the same chain. The chain then runs on either event, and you maintain it in one place instead of copying it into a second workflow.

This is what routing by package usually needs: a package can be known when the ticket is opened, or linked later, and both should send the ticket to the same team.

> **One routing table, two ways in.**
> *Ticket creation* and *Package selected*, both connected to a condition **Linked packages** *contains any of* your VIP packages → **Assign** your VIP specialist.

One rule comes with it: a node connected to several triggers only receives the data **all of them publish**. In the example above the condition tests *Linked packages*, which every trigger publishes, so it works from either entry point. A field only one trigger publishes — *Previous status*, or *Selected packages* — is not offered on a node the other trigger also leads to, because the ticket event that actually fired would not carry it. Connect that node to the one trigger that publishes it, and the field comes back.

## Order versus the AI auto-reply

For **Ticket creation**, **On chat message** and **Package selected**, the workflow's actions run **before** the AI auto-reply is scheduled, so an automation can adjust the ticket before the AI answers about it.

## Example

> **Only customer replies bump the priority.**
> *On chat message* → condition **Message author type** *is* **Customer** → **Set priority** to *High*.
> A team member's own reply leaves the priority alone, because the condition sends it down the *False* branch, which is connected to nothing.

> **The first team member to reply takes the ticket.**
> *On chat message* → condition **Message author type** *is* **Team member** → condition **Assignees** *is empty* → **Assign to a user** targeting *Whoever triggered this*.
> The assignee check is what makes it "first": once someone is assigned, later replies go down the *False* branch and nobody is reassigned.
