> ## Documentation Index
> Fetch the complete documentation index at: https://bettertickets.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Variables

> How the bot's templating syntax works, the {{random: ...}} tag, and which variables are available in each message you can customize.

Several fields you configure - ticket channel names, welcome messages, application titles, submission messages, DM messages support a small templating syntax so the text can adapt to who's involved. It's a purpose-built engine, not a general-purpose template library - it only implements the handful of features below, which keeps it fast and predictable. This page covers the syntax itself and exactly which variables you can use where.

## Output tags: `{{ }}`

Wrap a variable name in double curly braces to insert its value.

```text theme={null}
{{author.username}} opened this ticket.
```

If `author.username` is `alex`, this renders as:

```text theme={null}
alex opened this ticket.
```

Variables can be nested with dots, e.g. `{{author.username}}`, `{{server.name}}`. Spacing inside the braces doesn't matter - `{{author.username}}` and `{{ author.username }}` are the same. If a variable doesn't exist in the current context (see the tables below), the tag renders as empty rather than erroring.

There's also always a `{{now}}` variable available everywhere, even without any context - the current time, as a Unix timestamp in seconds. It's mainly useful inside a Discord timestamp tag:

```text theme={null}
Opened <t:{{now}}:R>
```

## Conditional tags: `{% if %}`

Wrap alternate text in `{% if condition %}...{% else %}...{% endif %}` to show different text depending on a variable. A condition can be:

* **Truthy** - just a variable name, e.g. `{% if fruit %}`. True when `fruit` has any value at all; false when it's unset.
* **Equality** - `{% if fruit == "apple" %}` or `{% if fruit != "apple" %}`. Quotes are optional for a single word (`fruit == apple` works too), but required if the value has spaces in it.

```text theme={null}
{% if fruit %}You picked {{fruit}}.{% else %}No fruit picked yet.{% endif %}
```

* If `fruit` is set to `apple`, this renders as: `You picked apple.`
* If `fruit` isn't set at all, this renders as: `No fruit picked yet.`

The right-hand side of `==`/`!=` can also be another variable (instead of a fixed word) as long as it's a dotted path - this is how you compare two things from the context against each other, like checking whether the ticket's author is the server owner:

```text theme={null}
{% if author.userId == server.ownerId %}
You are the server owner.
{% else %}
You are not the server owner.
{% endif %}
```

* If `author` is the `server` owner, this renders as: `You are the server owner.`

For more than two branches, chain `{% elsif %}` between `{% if %}` and `{% else %}`:

```text theme={null}
{% if status == "accepted" %}
Accepted
{% elsif status == "denied" %}
Denied
{% else %}
Pending
{% endif %}
```

A real example from the default channel name, using `claimer` (only set once a ticket is claimed):

```text theme={null}
{% if claimer %}✅{{author.username}}-{{claimer.username}}{% else %}{{ticketId}}-{{author.username}}{% endif %}
```

Both `%` symbols matter - `{% if claimer %}` is valid, `{% if claimer}` is not and will be left as literal, broken text.

Most malformed tags only affect themselves - a typo'd `{{ }}` or an unrecognized `{% %}` just renders as literal text in place, while everything else around it (before and after) still works normally. A broken `{% if %}` is the one exception worth knowing about: if it's missing its `{% endif %}` or has an unparseable condition, the parser can't tell which of the following tags were meant to be inside that conditional versus after it - so rather than guess, it treats **everything from that broken `{% if %}` to the end of the message** as raw text (anything *before* it still renders fine).

<Warning>
  In a channel name specifically, Discord's channel-name sanitizer will mangle any raw, unrendered `{% %}`/`{{ }}` text into something unreadable. Double-check your tags render as expected before saving, especially around `{% if %}` blocks.
</Warning>

## The random tag: `{{random: ...}}`

A custom tag that picks one option at random each time the message is rendered. Separate options with commas.

```text theme={null}
{{random: A staff member will be with you shortly., Please wait for a staff member to claim this ticket., Hello! Please wait\, we'll be right with you.}}
```

This is what the default welcome message uses, so new tickets don't all open with identical wording.

If an option needs to contain a literal comma (not a separator between options), escape it with a backslash: `\,`.

```text theme={null}
{{random: Hello! Please wait\, we'll be right with you., See you soon!}}
```

renders one of: `Hello! Please wait, we'll be right with you.` or `See you soon!` - two options, not three.

## What happens to invalid tags

Rendering never throws or shows broken output. A malformed `{{ }}` or an unrecognized `{% %}` just renders as literal text in place - everything else in the message, before and after it, still renders normally. A broken `{% if %}` (see the note above) is the exception: it and everything after it in the message falls back to raw text, though anything before it is unaffected. Always write complete tags, and preview the result before saving.

***

## Common variables

These are available in both ticket and application messages - only the meaning of `author` changes depending on context (the ticket's author, or the applicant).

<ResponseField name="now" type="number">
  Always available, everywhere - even outside ticket/application messages. The current time as a Unix timestamp in seconds, mainly for Discord timestamp tags like `<t:{{now}}:R>`.
</ResponseField>

<ResponseField name="author.userId" type="string">
  The Discord user ID of the person the message is about - the ticket's author, or the applicant.
</ResponseField>

<ResponseField name="author.username" type="string">
  Their Discord username.
</ResponseField>

<ResponseField name="author.globalName" type="string">
  Their display name.
</ResponseField>

<ResponseField name="reason" type="string">
  The reason tied to the current action, when there is one - a ticket close reason, or an application accept/deny reason.
</ResponseField>

<ResponseField name="server.id" type="string">
  The server's Discord ID.
</ResponseField>

<ResponseField name="server.name" type="string">
  The server's name.
</ResponseField>

<ResponseField name="server.ownerId" type="string">
  The server owner's Discord user ID.
</ResponseField>

<ResponseField name="server.memberCount" type="number">
  The server's member count.
</ResponseField>

## Where you can use variables

### Tickets

* **Channel name** <Badge color="orange" size="sm">Premium</Badge> - Channels page. Supports [common variables](#common-variables) and [ticket variables](#ticket-variables).
* **Welcome message** - General page. Supports [common variables](#common-variables) and [ticket variables](#ticket-variables).
* **Footer text** - General page. Supports [common variables](#common-variables) and [ticket variables](#ticket-variables).

#### Ticket variables

On top of the [common variables](#common-variables) above (where `author` means the ticket's author):

<ResponseField name="ticketId" type="string">
  The ticket category's identifier.
</ResponseField>

<ResponseField name="claimer.userId" type="string">
  The claimer's Discord user ID. Only set once claimed - always check with `{% if claimer %}` first.
</ResponseField>

<ResponseField name="claimer.username" type="string">
  The claimer's Discord username. Only set once claimed.
</ResponseField>

<ResponseField name="claimer.globalName" type="string">
  The claimer's display name. Only set once claimed.
</ResponseField>

**Default channel name:**

```text theme={null}
{% if claimer %}✅{{author.username}}-{{claimer.username}}{% else %}{{ticketId}}-{{author.username}}{% endif %}
```

**Default footer text:**

```text theme={null}
{% if claimer %}Opened by {{claimer.username}} for {{author.username}}.{% else %}Opened by {{author.username}}.{% endif %}
```

### Applications

* **Title / Description** - General page. Only [`server`](#common-variables) is available - there's no applicant yet when the panel is posted.
* **Submission message** <Badge color="orange" size="sm">Premium</Badge> - General page. Supports [common variables](#common-variables) and [application variables](#application-variables), plus `{{qa}}`.
* **Accept / deny DM message** - not yet user-editable. Supports [common variables](#common-variables) and [application variables](#application-variables) (no `qa`).

#### Application variables

On top of the [common variables](#common-variables) above (where `author` means the applicant):

<ResponseField name="reviewer.userId" type="string">
  The reviewer's Discord user ID. Only set on the decided-by footer - check with `{% if reviewer %}` first.
</ResponseField>

<ResponseField name="reviewer.username" type="string">
  The reviewer's Discord username. Only set on the decided-by footer.
</ResponseField>

<ResponseField name="reviewer.globalName" type="string">
  The reviewer's display name. Only set on the decided-by footer.
</ResponseField>

<ResponseField name="qa" type="string">
  The formatted question/answer list. Submission message only - see below.
</ResponseField>

<ResponseField name="decision" type="string">
  `"Accepted"` or `"Denied"`, once decided.
</ResponseField>

<ResponseField name="decidedAt" type="string">
  When the decision was made, once decided.
</ResponseField>

**Default submission message:**

```text theme={null}
**New application**
Author: <@{{author.userId}}>.
Username: {{author.username}}.
Nickname: {{author.globalName}}
{{qa}}
```

<Note>
  `{{qa}}` inserts every question and answer, formatted as a numbered list. You can only use it once per submission message - a second occurrence is rejected when you save, since it'd re-render a potentially large block twice.
</Note>

**Default accept/deny DM messages:**

```text theme={null}
Your application in **{{server.name}}** was accepted.
```

```text theme={null}
Your application in **{{server.name}}** was denied.
```

Every accepted or denied submission also gets a decided-by footer appended automatically (not user-editable), which is what adds the "Accepted by ... on ..." line to the submission message.
