# Placeholders

> The {path} tokens automation nodes can interpolate — ticket fields, trigger data, and what upstream actions produced — plus where they are supported and how they behave inside a JSON webhook body.

Source: https://joely.io/documentation/automations/placeholders
Last updated: 2026-08-31

Several action fields accept `{path}` placeholders (n8n-style), resolved against the trigger event and the ticket when the workflow runs. Write `Hi {customer.name}, ticket #{ticket.number} is on its way` and the customer reads their own name and number.

## Where they work

| Node | Fields |
|------|--------|
| [Post message](https://joely.io/documentation/automations/actions.md#post-message) | The message body and the custom author name |
| [Discord notification](https://joely.io/documentation/automations/actions.md#discord-notification) | Every text field of the embed |
| [Call a webhook](https://joely.io/documentation/automations/actions.md#call-a-webhook) | The custom request body |
| [Ask AI](https://joely.io/documentation/automations/actions.md#ask-ai) | The optional instruction |

Each of those config panels lists the tokens **that node** actually receives, and inserts them for you. The list is not the same everywhere: a node only receives what the nodes **before it** publish.

## Available on every trigger

| Token | Value |
|-------|-------|
| `{ticket.number}` | The ticket's number |
| `{ticket.subject}` | Its subject |
| `{ticket.status}` | Its current status |
| `{ticket.priority}` | Its priority |
| `{ticket.tags}` | Its tags |
| `{ticket.package_names}` | The names of the packages it is linked to |
| `{ticket.type}` | The form button it was opened from |
| `{customer.name}` | The customer who opened it |
| `{project.name}` | Your project's name |

## Added by the trigger

| Token | Trigger |
|-------|---------|
| `{trigger.from_status}` · `{trigger.to_status}` | Status changed |
| `{trigger.source}` | Ticket creation |
| `{trigger.message.content}` | On chat message |
| `{trigger.added_package_names}` | Package selected |

## Added by an upstream action

| Token | Published by |
|-------|--------------|
| `{message.content}` | Post message, Ask AI |
| `{choice.label}` | Post message, when the customer answered a choice |
| `{closure.scheduled_at}` · `{closure.skipped}` | Schedule closure |
| `{package.names}` · `{package.changed}` | Set package, Ask for the package |
| `{package.previous_names}` · `{package.unlinked}` | Unlink package |
| `{assignee.name}` · `{assignee.changed}` | Assign to a user |
| `{priority.new}` · `{priority.previous}` · `{priority.changed}` | Set priority |
| `{discord.sent}` · `{discord.dm_delivered}` · `{discord.dm_skipped}` | Discord notification |
| `{webhook.ok}` · `{webhook.status}` | Call a webhook |
| `{ai.confidence}` · `{ai.staff_handoff}` | Ask AI |

Opaque identifiers — a package id, a list of user uuids, a message id — are deliberately absent: they are [testable by a condition](https://joely.io/documentation/automations/conditions.md#fields-an-action-adds) but meaningless inside a message.

## Rules

- An **unknown path**, or one with no value, is left as written. A message never breaks because a token was mistyped.
- A [Discord embed](https://joely.io/documentation/automations/actions.md#discord-notification)'s live preview shows tokens **as you typed them**, unresolved. Only the real send resolves them.
- In a [webhook body](https://joely.io/documentation/automations/actions.md#call-a-webhook), placeholders are inserted as text and **escaped**, so a subject containing a quote or a line break cannot break the JSON. Keep each one inside quotes: `"{ticket.subject}"`.

## Example

A webhook body posting to your own API:

```json
{
  "ticket": "{ticket.number}",
  "subject": "{ticket.subject}",
  "priority": "{ticket.priority}",
  "customer": "{customer.name}",
  "moved_to": "{trigger.to_status}"
}
```
