Expressions
Kite supports performing calculations and transformations on data using expressions. Expressions are available in the Calculate Value block (which also supports multi-line input) and in every placeholder surrounded by {{ and }}.
Expression syntax
Expressions are powered by the Expr language, you can learn more about the features and syntax here.
Available variables
Kite provides the following variables in the expression environment:
user:
id: string
username: string
discriminator: string
display_name: string
avatar_url: string
banner_url: string
mention: string
is_bot: bool
created_at: int # When the account was created, as a Unix timestamp in seconds
role_ids?: []string
nick?: string
# Only filled inside a server
joined_at: int # When the user joined the server, as a Unix timestamp in seconds
top_role: # The highest role of the user, or @everyone without roles
id: string
name: string
mention: string
color: string # Like #5865f2, empty if the role has no color
hoist: bool # Whether the role is shown separately in the member list
mentionable: bool
role_mentions: string # Mentions of all roles of the user, highest first
role_names: string # Names of all roles of the user, highest first, separated by commas
role_count: int
color: string # Color of the highest role that has one, like #5865f2
is_booster: bool
boosting_since: int # Unix timestamp in seconds, 0 if the user isn't boosting
is_timed_out: bool
timeout_until: int # Unix timestamp in seconds, 0 if the user isn't timed out
is_owner: bool # Whether the user owns the server
is_admin: bool # Whether the user owns the server or has the Administrator permission
permissions: []string # Lowercased permission names, like ban_members or manage_messages
message?: # For message events
id: string
channel_id: string
content: string
author: # Same fields as user, empty for reactions and deleted messages
id: string
username: string
reactions: # Empty for message events, get the message with a block to count its reactions
- emoji:
id: string # Empty for a unicode emoji
name: string
count: int
mentions: # Users the message pings, with the same fields as user. Includes the author of the message it replies to if the reply pings them
- id: string
username: string
channel:
id: string
name: string
mention: string
type: string # text, voice, category, announcement, thread, stage, forum, media, dm, group_dm, or unknown for newer types
guild_id: string # The server the channel is in, empty for direct messages and if it isn't known
category_id: string # Empty outside of a category, for a thread the category of its channel
category_name: string
guild?: # For events and interactions inside a server, elsewhere guild is empty and so are its fields
id: string # The id of the server
name: string
icon_url: string # Empty if the server has no icon
member_count: int
boost_count: int
owner_id: string # The id of the user that owns the server
boost_level: int # 0 to 3
created_at: int # When the server was created, as a Unix timestamp in seconds
banner_url: string # Empty if the server has no banner
description: string
vanity_url: string # The custom invite link, empty if the server has none
role_count: int # Without @everyone
channel_count: int # Without categories and threads
emoji_count: int
rules_channel: # Empty if the server has no rules channel
id: string
name: string
mention: string
system_channel: # Where Discord posts join and boost messages, empty if turned off
id: string
name: string
mention: string
interaction?: # For commands and interactive components
id: string
value?: string # The value of the picked option in a select menu, or the ID of the picked user, role or channel
values?: []string # All picked values or IDs if the select menu allows picking more than one
components?: # For modal submissions, by input identifier
identifier:
value: string # What input('identifier') returns
values: []string # What inputs('identifier') returns
app:
user: # Access the underlying user of the app
id: string
mention: string
guild_count: int # The number of servers your app is in
member_count: int # The members of all those servers added up
latency: int # The number of milliseconds Discord took to answer your app's last heartbeat
user_install_count: int # Discord's approximate number of users that installed your app
There are also a few special variables for accessing dynamic variables:
arg('name') # Access value of a command argument
input('identifier') # Access value of a modal input, the first one if several options were picked
inputs('identifier') # Access all picked options of a modal input as a list
picked('identifier') # Access the member, role or channel picked in a modal select, or the uploaded file, the first one if there are several
pickedAll('identifier') # Access all members, roles or channels picked in a modal select, or all uploaded files, as a list
result('id') # Access the result of a previous block
calculate('5 + 5') # Calculate arithmetic written as text, like a message, nothing if it isn't only math
duration('1d 12h') # Read a length of time written as text, like a command argument, nothing if it isn't one
The ID of a block is shown in its settings. New blocks get a short ID that says what they are, like message_k3f for a Create Message block. Older blocks have IDs like owlspush, which keep working.
top_role, role_names, color, is_owner, is_admin and permissions need the roles of the server. They work for user, members from command arguments, and origin.user and previous.user in sub-flows. A member from result('id'), like the result of a Get Member block, or from a variable doesn't know its server, so using them there fails the flow instead of returning a wrong answer. They also fail in a server your app isn't in, where only user-installed commands run.
guild.member_count follows members joining and leaving while your app has a Member Join or Member Leave event listener. Without one, it's the count from when your app last connected to Discord.
app.member_count adds up the member count of each server like guild.member_count, so a user who is in several of your app's servers is counted once for each. app.user_install_count is updated about once a day, and is 0 until Discord first reports it after your app is created. app.latency is 0 while your app connects to Discord. Until Discord answers the first heartbeat after that, it's how long Discord took to accept the connection.
In sub-flows, origin and previous give access to the interaction or event from before the sub-flow, e.g. origin.user.id. Outside of sub-flows they are the current interaction or event, so origin.user.id is the same as user.id.
Examples
When using the Calculate Value block, you must omit the {{ and }} from the expression.
Get a command argument
This will return the value of the myarg argument passed to the command.
{{ arg('myarg') }}
Get a user's display name
This will return the display name of the user who clicked the button or triggered the command or event.
{{ user.display_name }}
Get the message content
This will return the content of the message that was sent.
{{ message.content }}
Check the reaction emoji
In a Reaction Add or Reaction Remove event listener, this will return true if the reaction was a 👍. For a custom emoji, compare emoji.id instead.
{{ emoji.name == "👍" }}
Check if a user has a role
This will return true if the user has the role with the ID 123.
{{ "123" in user.role_ids }}
Get the selected option
This will return the value of the option that the user picked in a select menu.
{{ interaction.value }}
If the select menu allows picking more than one option, this will return true if the user picked the option with the value option-a.
{{ "option-a" in interaction.values }}
In a user, role, mentionable or channel select, the values are IDs. This mentions the channel that was picked:
<#{{ interaction.value }}>
Welcome a new member
This will greet a member with the name of the server and their member number.
Welcome to {{ guild.name }}, {{ user.mention }}! You're member #{{ guild.member_count }}.
Check picked modal options
If a modal input with the identifier colors allows picking more than one option, inputs('colors') returns all picked values as a list. This will return true if the user picked red.
{{ 'red' in inputs('colors') }}
This will return how many options were picked.
{{ len(inputs('colors')) }}
And this will list them separated by commas, e.g. red, blue.
{{ join(inputs('colors'), ', ') }}
Use picked members, roles, and channels
For a user, role, mentionable or channel select in a modal, input(...) and inputs(...) return IDs. picked(...) returns the picked member, role or channel itself, with the same fields as user, a role or channel, and pickedAll(...) all of them as a list. On its own it shows as a mention. If a modal has a user select with the identifier reported, this will name the reported member.
{{ picked('reported').display_name }} was reported by {{ user.display_name }}.
This will list the names of all picked roles, separated by commas.
{{ join(map(pickedAll('roles'), #.name), ', ') }}
For a file upload in a modal, picked(...) returns the uploaded file, which shows as its URL. This will name the file uploaded with the identifier image and its size in bytes.
{{ picked('image').filename }} ({{ picked('image').size }} bytes)
Check when a user was created
This will show when the user's account was created, as a Discord timestamp.
<t:{{ user.created_at }}:f>
This will return true if the account is less than 7 days old, which is useful for anti-raid checks. user.joined_at works the same way for how long someone has been in the server.
{{ user.created_at > now().Unix() - 7 * 86400 }}
Only run in a category
This will return true if the command was used in a channel of the category named Tickets. Use channel.category_id instead to keep working when the category is renamed.
{{ channel.category_name == "Tickets" }}
This will return true inside a thread.
{{ channel.type == "thread" }}
Check if a user owns the server
This will return true if the user is the owner of the server.
{{ user.is_owner }}
Check the permissions of a user
This will return true if the user is allowed to ban members. The names are listed below.
{{ "ban_members" in user.permissions }}
user.permissions are the permissions the roles of the user give them in the server. Permissions a single channel grants or denies aren't included. Administrators and the owner of the server have every permission, and user.is_admin is true for them.
| Permission | Name in the server settings |
|---|---|
create_instant_invite | Create Invite |
kick_members | Kick, Approve, and Reject Members |
ban_members | Ban Members |
administrator | Administrator |
manage_channels | Manage Channels |
manage_guild | Manage Server |
add_reactions | Add Reactions |
view_audit_log | View Audit Log |
priority_speaker | Priority Speaker |
stream | Video |
view_channel | View Channels |
send_messages | Send Messages and Create Posts |
send_tts_messages | Send Text-to-Speech Messages |
manage_messages | Manage Messages |
embed_links | Embed Links |
attach_files | Attach Files |
read_message_history | Read Message History |
mention_everyone | Mention @everyone, @here, and All Roles |
use_external_emojis | Use External Emoji |
view_guild_insights | View Server Insights |
connect | Connect |
speak | Speak |
mute_members | Mute Members |
deafen_members | Deafen Members |
move_members | Move Members |
use_vad | Use Voice Activity |
change_nickname | Change Nickname |
manage_nicknames | Manage Nicknames |
manage_roles | Manage Roles |
manage_webhooks | Manage Webhooks |
manage_guild_expressions | Manage Expressions |
use_application_commands | Use Application Commands |
request_to_speak | Request to Speak |
manage_events | Manage Events |
manage_threads | Manage Threads and Posts |
create_public_threads | Create Public Threads |
create_private_threads | Create Private Threads |
use_external_stickers | Use External Stickers |
send_messages_in_threads | Send Messages in Threads and Posts |
use_embedded_activities | Use Activities |
moderate_members | Timeout Members |
view_creator_monetization_analytics | View Server Subscription Insights |
use_soundboard | Use Soundboard |
create_guild_expressions | Create Expressions |
create_events | Create Events |
use_external_sounds | Use External Sounds |
send_voice_messages | Send Voice Messages |
set_voice_channel_status | Set Voice Channel Status |
send_polls | Create Polls |
use_external_apps | Use External Apps |
pin_messages | Pin Messages |
bypass_slowmode | Bypass Slowmode |
Point to the rules channel
This will mention the rules channel the server has set in its settings, and is empty if it has none.
Please read {{ guild.rules_channel }} before posting.
Show the roles of a user
This will mention the highest role of the user, and then all of their roles.
{{ user.top_role.mention }}
{{ user.role_mentions }}
Show bot stats
This will show how many servers your app is in and its ping, for commands like /botinfo or /ping.
In {{ app.guild_count }} servers with {{ app.member_count }} members, ping {{ app.latency }}ms
Do some math
This will return the result of the expression.
{{ 1 + 1 }}
To calculate math that members write, like (3 * 4) - 2 in a message, use calculate. It knows + - * / % ^, x and × for times, ÷, and parentheses, and returns nothing for text that isn't only math.
{{ calculate(message.content) }}
Decode a JSON response
This will return the value of the somefield field in the JSON response of an HTTP request block with the id http_k3f.
{{ result('http_k3f').data().somefield }}
Timestamps
Discord timestamps are shown in the local timezone of whoever reads the message. Wrap a Unix timestamp in <t:...:style> and pick one of the styles below.
In the message editor, the Time tab of the placeholder picker inserts these for you, for the time the message is sent, a specific date, or some time after it is sent.
<t:{{ now().Unix() }}:t> # Short time, like 5:36 PM
<t:{{ now().Unix() }}:T> # Long time, like 5:36:12 PM
<t:{{ now().Unix() }}:d> # Short date, like 01/02/2026
<t:{{ now().Unix() }}:D> # Long date, like January 2, 2026
<t:{{ now().Unix() }}:f> # Short date and time, like January 2, 2026 5:36 PM
<t:{{ now().Unix() }}:F> # Long date and time, like Friday, January 2, 2026 5:36 PM
<t:{{ now().Unix() }}:R> # Relative time, like 2 minutes ago
This will show a countdown that ends one hour from now.
<t:{{ now().Add(duration("1h")).Unix() }}:R>
duration reads lengths of time like 1h30m, 2h 30m, 1d 12h, 2 hours or 1 day and 30 minutes. The units are s, m, h, d and w, or written out like minutes, in any case, so M is minutes too. For text that isn't a length of time, like a typo in a command argument, it returns nothing, which you can check before using it.
{{ duration(arg('time')) == nil ? "Use a time like 30m or 2h" : "In " + string(duration(arg('time')).Seconds()) + " seconds" }}