---
title: "Workflow reference"
description: "Every workflow trigger, condition field, frequency, step, recipient, placeholder, limit, run outcome, and starter library workflow, as tables."
url: "https://support.outpostplatform.com/service-desk/workflow-reference/"
product: "depot"
type: "reference"
status: "draft"
ai_summary: "Workflow reference tables: triggers, condition fields and codes (ticket.status_category, ticket.has_endpoint), operators, How often modes, When it runs, steps and the triggers that allow them, recipients, webhook rules, placeholders, limits (50 steps, 3 levels, 2 AI steps, 200 actions an hour, 3 client emails per ticket a day), run outcomes and reasons, and the six starter library workflows."
availability:
  plans: ["enterprise", "msp"]
  permission: "automation:manage"
  preview: false
source: "https://gitlab.com/outpostplatform/docs/-/edit/main/src/content/docs/service-desk/workflow-reference.md"
license: "CC BY 4.0"
---

# Workflow reference

This page lists every option a [workflow](/service-desk/workflows/) has, for
looking up one value. To build a workflow, see
[Build a workflow](/service-desk/build-a-workflow/).

## Triggers

| Trigger | Settings | Notes |
| --- | --- | --- |
| **When something happens to a ticket** | One or more of **created**, **updated**, **replied to**. | Runs as part of the ticket change. A workflow with an AI step runs a few moments after it instead. |
| **Keep checking open tickets** | None. | Every 5 minutes, over tickets whose status type is not Closed: open tickets, and Solved tickets with activity in the last 90 days. The trigger summary reads **Checks open and recently solved tickets every 5 minutes**. |
| **On a schedule** | **Repeat** (**Every day**, **Every week**, **Every month**, or **Advanced (cron)**), **On** or **On day** (1 to 28), **At**, and **Time zone**. | **Advanced (cron)** takes five fields: minute, hour, day of month, month, day of week. Each run happens once in every client in scope. A client that cannot be reached at that moment is skipped for that run. |
| **When tickets pile up** | **Tickets that share** (**An endpoint** or **A requester**), **At least** (2 to 50), **Within days** (1 to 365), **Then wait days** (0 to 365). | Counts tickets created within the last **Within days**, leaving out tickets that workflows created. A group that fires waits **Then wait days** before it can fire again. Blank means the same as **Within days**. |

**Only count tickets** narrows a pile-up count by **With priority**, **In
status**, **Of type**, and **With any of these tags**. Blank counts every
ticket.

## Conditions

Conditions are built from a field, an operator, and a value, inside **all**
or **any** groups. A condition tree can be up to 8 levels deep and hold up to
100 conditions. The same fields are used by
[SLA policy conditions](/service-desk/sla/#create-an-sla-policy), which take
them as JSON with the codes below.

| Code | Console label | Type | Notes |
| --- | --- | --- | --- |
| `ticket.status` | Status | Text | A status code: `new`, `open`, `pending`, `on_hold`, `solved`, `closed`, or the code of a status you added. |
| `ticket.status_category` | Status type | Text | `new`, `open`, `pending`, `hold`, `solved`, or `closed`. Matches every status of that type, including statuses you added. |
| `ticket.priority` | Priority | Text | `normal`, `high`, `urgent`, or `critical`. |
| `ticket.type` | Type | Text | The ticket's category code, such as `incident` or `request`, or the code of a category you added. |
| `ticket.queue_id` | Queue | Text | Chosen by name in the builder. |
| `ticket.group_id` | Group | Text | Chosen by name in the builder. |
| `ticket.assignee_id` | Assignee | Text | Chosen from your team. Empty means unassigned. |
| `ticket.is_assigned` | Assigned | True or false | |
| `ticket.has_endpoint` | Linked to an endpoint | True or false | True when the ticket is linked to an endpoint. |
| `ticket.subject` | Subject | Text | |
| `ticket.channel` | Channel | Text | `tray`, `email`, `console`, `api`, or `automation`. |
| `ticket.tags` | Tags | List | |
| `ticket.requester_confidence` | Requester match | Text | `attested`, `unclaimed`, `email`, or `manual`. |
| `ticket.message_count` | Messages | Number | |
| `ticket.reopen_count` | Times reopened | Number | |
| `ticket.age_minutes` | Ticket age | Number | Time-based. |
| `ticket.minutes_since_update` | Time since last update | Number | Time-based. |
| `ticket.minutes_since_public_reply` | Time since our last public reply | Number | Time-based. |
| `ticket.sla_minutes_remaining` | SLA time remaining | Number | Time-based. Minutes until the ticket's next SLA due time. |
| `ticket.sla_breached` | SLA breached | True or false | Time-based. |
| `requester.email`, `requester.name` | Requester email, Requester name | Text | |
| `assignee.name` | Assignee name | Text | |
| `endpoint.hostname`, `endpoint.site` | Endpoint name, Endpoint site | Text | |
| `endpoint.connected`, `endpoint.metered` | Endpoint online, Endpoint on a metered connection | True or false | |

In the builder, statuses and status types show as **New**, **Open**,
**Pending**, **On hold**, **Solved**, and **Closed**, and time-based fields
take a number and a unit: minutes, hours, or days. In JSON they are minutes.
Time-based fields change without anyone touching the ticket, so use them with
**Keep checking open tickets**.

| Operator code | Console label |
| --- | --- |
| `equals`, `not_equals` | is, is not |
| `contains`, `not_contains` | contains, does not contain |
| `starts_with`, `ends_with` | starts with, ends with |
| `in`, `not_in` | is one of, is none of |
| `is_empty`, `is_not_empty` | is empty, is not empty |
| `greater_than`, `at_least` | is more than, is at least |
| `less_than`, `at_most` | is less than, is at most |
| `is_true`, `is_false` | is yes, is no |

## How often

| Choice | Triggers | Settings |
| --- | --- | --- |
| **Once per ticket** | Ticket events, **Keep checking open tickets** | None. The default. |
| **A few times per ticket** | Ticket events, **Keep checking open tickets** | **Up to** 2 to 10 times. **At most every** 15 minutes or more. |
| **Repeatedly while it matches** | **Keep checking open tickets** | **At most every** 15 minutes or more, and 60 minutes or more when a step emails the client. |
| **Every time** | Ticket events | None. |

**Start counting again when the ticket stops matching** is available with
**Once per ticket** and **A few times per ticket**. Schedule and pile-up
workflows have no **How often** setting: a schedule runs once per occurrence
in each client, and a pile-up group waits **Then wait days**.

## When it runs

| Choice | Hours used |
| --- | --- |
| **Any time** | Every hour. |
| **During a business hours schedule** | The named business schedule, looked up by name in each client. A client without it skips the workflow. |
| **During the ticket's SLA hours** | The schedule of the policy of the ticket's running SLA timer, else the client's default schedule. |

Schedule workflows always use **Any time**.

## Steps

| Step | What it does | Triggers |
| --- | --- | --- |
| **Change a field** | Sets the status, priority, type, queue, group, or assignee. Queue and group are chosen by name. | Ticket events, **Keep checking open tickets** |
| **Add a tag**, **Remove a tag** | Adds or removes one tag. | Ticket events, **Keep checking open tickets** |
| **Add an internal note** | Adds an internal note signed with the workflow's name. Clients never see it. | Ticket events, **Keep checking open tickets** |
| **Email an address** | Sends a plain-text email on the ticket's thread to the requester, the assigned technician, or an address you type. To reach your team in the console too, use **Notify people**. Counts toward the client email limit, whoever it goes to. | Ticket events, **Keep checking open tickets** |
| **Call a webhook** | Posts a body to an `https://` URL. See [Webhook calls](#webhook-calls). | Ticket events, **Keep checking open tickets** |
| **Notify people** | Notifies the recipients you choose. See [Recipients](#recipients). | All |
| **Create a ticket** | Opens a ticket with a **Subject**, **Message**, **Priority**, **Status**, **Queue**, **Group**, **Assign to**, and **Tags**, optionally from a **Template**. **Link the new ticket to the same endpoint** links it to the ticket's or the pile-up group's endpoint. The new ticket has no requester, so no client is emailed about it. | All |
| **If / Else** | Runs **Then** when **A condition** is true or **The AI's answer** is one you select, and **Else** otherwise. | Ticket events, **Keep checking open tickets** |
| **Ask AI** | Chooses an answer from your list and can write a note, a draft reply, and tags. See [AI steps in workflows](/service-desk/workflow-ai-steps/). | Ticket events, **Keep checking open tickets** |

When an **Email an address** step emails an address, the built-in
[ticket notification email](/service-desk/ticket-notifications/) for the same
change is not also sent to that address. Field and tag changes are made as
part of the ticket change. Emails and webhooks are sent after it, and failed
sends are retried. An email or webhook that could not be sent within
24 hours is dropped rather than sent late.

A ticket a workflow creates is handled like any other new ticket, so its SLA
timers start and workflows that run on new tickets see it. Tickets created by
workflows running on workflow-created tickets stop after 2 levels.

### Recipients

| Recipient | Who | How |
| --- | --- | --- |
| **The requester** | The ticket's requester. | Email |
| **Everyone copied on the ticket** | Every participant with the CC role. | Email |
| **The client's primary contact** | The person who holds the client's **Primary contact** tag, when they have a confirmed email address. | Email |
| **The assigned technician** | The ticket's assignee. | Console notification |
| **Members of the ticket's group** | Everyone in the ticket's group. | Console notification |
| **Everyone with a role** | Everyone who holds the role you add. | Console notification |
| **A specific person** | One person on your team. | Console notification |

Each address is emailed once per step, and a step's client emails count once
toward the client email limit. Your team's notifications go to the
console bell, and by email too when the person's **Workflow notices**
preference includes email. See [Notifications](/depot/notifications/).
Schedule and pile-up workflows can notify only **Everyone with a role** and
**A specific person**.

### Webhook calls

- The URL must use `https://`. Addresses that resolve to a private, loopback,
  or link-local network are refused, so a webhook cannot reach machines inside
  the network your Outpost runs in.
- The body is sent as `application/json` when it is valid JSON, otherwise as
  plain text. Placeholder values in a webhook body are escaped as JSON string
  text, so put each placeholder inside quotes:
  `{"ticket": "{{ticket.reference}}", "subject": "{{ticket.subject}}"}`.
- Bodies over 64 KB are refused.
- Each request carries an `X-Outpost-Delivery` header that stays the same when
  a request is retried, so your receiver can ignore a repeat, and an
  `X-Outpost-Ticket` header with the ticket's ID.
- A `2xx` response is a success. A `4xx` response, other than `408` and `429`,
  fails without a retry. Redirects are not followed. Timeouts (10 seconds),
  `408`, `429`, and `5xx` responses are retried.

## Placeholders

A workflow that uses a placeholder not in this list, or one its trigger cannot
fill, is refused when you save it.

| Placeholder | Value | Triggers |
| --- | --- | --- |
| `{{ticket.reference}}`, `{{ticket.subject}}`, `{{ticket.status}}`, `{{ticket.priority}}`, `{{ticket.queue}}`, `{{ticket.url}}` | The ticket's details. | Ticket events, **Keep checking open tickets** |
| `{{requester.name}}`, `{{requester.first_name}}`, `{{requester.email}}` | The requester's details. | Ticket events, **Keep checking open tickets**, and pile-ups grouped by requester |
| `{{assignee.name}}` | The assignee's name. | Ticket events, **Keep checking open tickets** |
| `{{endpoint.hostname}}`, `{{endpoint.site}}` | The endpoint's details. | Ticket events, **Keep checking open tickets**, and pile-ups grouped by endpoint |
| `{{client.name}}` | The client's name. | All |
| `{{run.date}}` | The run's date as `2026-11-02`, in the schedule's time zone, or the client's default schedule's time zone for other triggers. | All |
| `{{run.month}}` | The run's month, such as `November 2026`. | All |
| `{{pattern.count}}` | How many tickets are in the group. | **When tickets pile up** |
| `{{pattern.days}}` | The **Within days** setting. | **When tickets pile up** |
| `{{pattern.references}}` | The group's ticket references, newest first, up to 20. | **When tickets pile up** |
| `{{ticket.first_message}}` | The ticket's first message, up to 4,000 characters. | **Ask AI** question only |

## Limits

| Limit | Value |
| --- | --- |
| Name | 120 characters |
| Description | 1,000 characters |
| **Run order** | 0 to 100,000. The default is 100. |
| Steps | 50, counting every step inside branches and fallbacks |
| Nesting | 3 levels |
| **Ask AI** steps | 2 per workflow |
| AI answers | 2 to 12 |
| AI allowed tags | 20 |
| AI question | 4,000 characters |
| Conditions | 100 per workflow, 8 levels deep |
| **Actions per workflow per hour** | 200 by default, 1 to 10,000, across all clients. Over the limit, the workflow is paused. |
| **Client emails per ticket per day** | 3 by default, 0 to 50, counted per UTC day across all workflows. Over the limit, further email steps on the ticket are skipped. `0` stops all workflow email to clients. |
| Test results | 25 matches per client, 200 in total |

Actions are the steps that change something: field changes, tags, notes,
notifications, emails, webhooks, and created tickets. The two bold limits apply
to the whole practice. Change them under **Safety limits** on the
**Workflows** page. See
[Change the safety limits](/service-desk/build-a-workflow/#change-the-safety-limits).
They are also available through the `update_workflow_settings` MCP tool and
`PUT /api/v1/workflows/settings`.

## Run outcomes and reasons

The **Runs** tab shows an outcome for each run, and a reason for skips and
fallbacks.

| Outcome | Meaning |
| --- | --- |
| **Ran** | The workflow acted. |
| **Skipped** | It did not act. |
| **Failed** | It could not finish. |
| **Stopped by a loop guard** | Outpost stopped it to prevent a loop. |
| **Took the AI fallback** | An AI step fell back and its fallback steps ran. |

| Reason | Meaning |
| --- | --- |
| **Conditions did not match** | A ticket event did not meet the conditions. |
| **Outside its run window** | Outside the hours in **When it runs**. |
| **A named item is missing in this client** | A queue, group, schedule, or template is not in this client. Only that step is skipped. |
| **Already ran as often as allowed** | The **How often** limit was reached. |
| **All workflows are paused** | **Pause all workflows** is on. |
| **This workflow is paused** | The workflow is paused. |
| **Client email limit reached for today** | The ticket reached the client email limit. Only the email step is skipped. |
| **Too many actions in an hour; paused** | The workflow went over the action limit and was paused. |
| **An AI branch tried to close the ticket** | A branch on an AI answer tried to set a solved or closed status. |
| **The ticket no longer exists** | The ticket was deleted before the workflow ran. |

The AI fallback reasons are listed in
[AI steps in workflows](/service-desk/workflow-ai-steps/#the-fallback).

## Starter library

**Start from library** adds one of these, turned off and scoped to every
client.

| Workflow | Trigger | What it does |
| --- | --- | --- |
| **Waiting on client: nudge, then close** | **Keep checking open tickets** | When a ticket of status type Pending has had no public reply from you for 3 days, emails the requester a reminder. If it is still waiting at 7 days, solves the ticket and emails the requester that it is closed. Runs up to 2 times per ticket, at most every 4 days, and starts counting again when the ticket stops matching. |
| **Untouched urgent ticket: escalate after 24 hours** | **Keep checking open tickets** | An urgent or critical ticket of status type New or Open with no update for 24 hours is tagged `escalated`, and everyone with the Support Manager role is notified. Once per ticket. |
| **Monthly backup check** | **On a schedule** | At 09:00 on the first of every month, `America/New_York`, opens a ticket (in MSP mode, one in each client in scope) titled with the client's name and the month, tagged `backup-check`. Change the time zone to yours. |
| **Repeat device review** | **When tickets pile up** | When one endpoint has 3 tickets within 60 days, opens a review ticket linked to the endpoint that lists the tickets, tagged `repeat-device`. |
| **AI triage note on new tickets** | **When something happens to a ticket** (created) | Asks AI whether a new ticket needs a technician now or is routine, adds an internal note and the tag `ai-triaged`, and raises a Low or Normal ticket to High when it needs a technician now; it never lowers an Urgent or Critical one. If AI cannot answer, notifies everyone with the Support Manager role to triage it by hand. Uses AI. |
| **SLA near breach: nudge the assignee** | **Keep checking open tickets** | When the SLA of a ticket of status type New or Open is due within 60 minutes and not yet breached, notifies the assignee, or everyone with the Support Manager role when nobody is assigned. Once per ticket, and again if the ticket stops matching and comes back. |

## Related

- [Workflows](/service-desk/workflows/)
- [Build a workflow](/service-desk/build-a-workflow/)
- [AI steps in workflows](/service-desk/workflow-ai-steps/)
