K
Docs
API Reference

🧩 Pieces

Every Piece on the Canvas — documented with config fields, output ports, sparks they add, JSON examples, and a TypeScript equivalent so you understand exactly what each one does.

🧩 78 Pieces — the Base Set and every expansion
📬

Messages

5 Pieces
🧠

Logic

7 Pieces
🔀
Condition
base.condition

The decision maker! Checks if something is true or false, then sends the flow down one of two paths. It's how your bot becomes smart — 'if this user is an admin, do X, otherwise do Y'.

Base Set
truefalse
🔗
Merge
base.merge

A visual hub for converging flows. Wire both Condition outputs (or any parallel paths) into this piece, then connect one output to your shared downstream chain. The runner already supports multiple incoming edges — Merge makes that pattern obvious on the canvas.

Base Set
🎯
Coalesce
base.coalesce

Sets a spark from a primary template, or falls back when the primary is empty. Perfect for /profile user_p: use the mentioned user's ID when provided, otherwise the invoker's ID — without duplicating Save Sparks on each branch.

Base Set
⏳
Wait
base.wait

Pumps the brakes! Pauses the flow for a set number of milliseconds before continuing. Useful for adding a dramatic pause before a big reveal, or spacing out multiple messages so they don't all appear at once.

Base Set
📌
Save Spark
base.variable

Creates or updates a named spark that holds a typed value — text, number, yes/no, list, or object. Update existing sparks in place: numbers use Add/Subtract/Multiply/Divide, text uses append/prepend, lists use Add to list / Prepend to list (single items or rows), booleans support toggle. The spark is then available everywhere downstream.

Base Set
{{score}}
🔁
Loop
base.loop

Repeat after me! Takes a list and runs the connected flow once for each item in it. Great for sending messages to multiple users, processing a list of scores, or doing anything that needs to happen more than once.

Base Set
{{item}}
eachdone
↩️
Return
base.return

Ends the custom piece's flow and hands named values back to whoever called it. The fields shown here come from the returns you defined on this custom piece.

Base Set
🗄️

Vault

13 Pieces
📖
Read from Vault
base.db.read

Fetches rows from one of your Vault tables. Pick a table, choose which column to match on (like user_id), and give the result a name — it becomes a ✨ Spark you can drop into any later piece.

The Vault
{{result}}{{result.length}}{{result.0}}
foundempty
✏️
Write to Vault
base.db.write

Saves a row to one of your Vault tables. Pick a table, fill in each column's value, and optionally turn on 'update if exists' to avoid duplicate rows — perfect for tracking coins, XP, or anything per-user.

The Vault
🗑️
Delete from Vault
base.db.delete

Permanently removes rows from one of your Vault tables. Pick the table, choose which column to match on, and only those rows get deleted. No filter? The whole table is wiped — so be careful!

The Vault
🔐
All-or-Nothing
vault.safe

A safe for your Vault changes. Put Vault pieces on its Inside path and they count as one: if everything works, every change is saved at once and the flow carries on from All saved. If anything goes wrong — or a ✋ Call it Off runs — every change inside is undone like it never happened, and the flow takes Undone instead. Perfect for shops, trades and paying someone: nobody ends up with coins from nowhere.

The Vault
insidedoneoops
✋
Call it Off
vault.abort

Changed your mind? Inside an 🔐 All-or-Nothing box, this undoes every Vault change the box made and jumps to its Undone path, carrying your reason along as {{oops.reason}}. Pair it with a Condition: "not enough coins? call it off".

The Vault
➕
Bump a Number
vault.add

Changes one number on one row — coins, XP, lives, streaks — in a single step nobody can sneak between. Set "Never go below" and it simply refuses to go under it: perfect for spending, since two clicks at once can't double-spend. If the row doesn't exist yet it's created for you, starting from the column's default.

The Vault
outputnot_enough
🔎
Get One Row
vault.get

Like Read from Vault, but for when you want one row: the first one that matches. Its columns become sparks directly — {{row.coins}} instead of {{result.0.coins}}. Filter with = ≠ > < ≥ ≤, contains or starts with.

The Vault
foundmissing
🏆
Top List
vault.top

Sorts a table by one column and hands back the top rows — a leaderboard in one piece. Each row gets a place number too ({{item.place}} in a Loop), and empty values always sort last. Filter it to make per-team or per-server boards.

The Vault
foundempty
🥇
Find Rank
vault.rank

Finds one row's place when the table is sorted by a column — without fetching the whole leaderboard. Ties share a place, like a real scoreboard. Gives you {{rank.place}}, {{rank.total}} and {{rank.value}}.

The Vault
foundmissing
🧮
Tally
vault.tally

Adds things up across a table: how many rows, the total of a column, its average, lowest or highest. Filter it to tally just one team, one server or one kind of thing. Great for "coins in circulation" or "how many warnings has this person had".

The Vault
🎲
Random Row
vault.random

Pulls one row out of a table at random — a random quote, a random winner, a random card from a deck. Filter it to pick only from some rows. Its columns become sparks: {{pick.text}}.

The Vault
foundempty
🔁
Update Matching
vault.update

Changes the rows that match your filter, all at once: reset everyone's weekly score, move a whole team, add 5 XP to every member of a guild. Number columns can + Add or − Sub just like Write to Vault. It never creates rows, so nothing appears out of thin air.

The Vault
outputnone
⏳
Cooldown
vault.cooldown

Lets something happen once per time window: a /daily reward, a /work shift, a /rob that needs to cool off. The first time it's Ready and starts the timer; until the time is up it's Cooling, with {{cooldown.left}} saying how long to wait. No table needed — the Vault keeps track for you.

The Vault
readycooling
🎲

Utility

16 Pieces
🎲
Random Pick
base.random

Reaches into a bag and pulls out one random item. Great for random jokes, quotes, prizes, challenge assignments, or anything where variety is the spice of life.

Base Set
{{pick}}
📋
Log
base.log

Writes a message to your bot's Activity log. Use info for breadcrumbs, warn for odd-but-okay moments, and error when something went wrong but you handled it in the flow. Great for debugging without spamming Discord.

Base Set
✒️
Format Text
base.format

Constructs a piece of text by filling in a template with live values. Combine static text with Sparks to build dynamic messages, labels, or anything that needs to be assembled from parts.

Base Set
{{formatted}}
🕐
Format Time
base.format_time

Formats a date/time spark into human-readable text, ISO, a custom pattern, or Discord's native `<t:unix:style>` timestamps (t, T, d, D, f, F, R). Leave the input empty to use the current time when the flow runs.

Base Set
🔢
Math
base.math

Runs an arithmetic expression and stores the answer as a Spark. Add scores, calculate percentages, multiply rewards — if numbers are involved, this piece does the work.

Base Set
{{total}}
🔃
Sort List
base.sort

Takes a list and sorts it ascending or descending. If the list came from a Vault read, it auto-detects the table schema and lets you pick which column to sort by — no guessing field names. The sorted result becomes a ✨ Spark you can loop over or show directly.

Base Set
{{sorted}}{{sorted.length}}{{sorted.0}}
✂️
Split Text
base.text.split

Splits text into a list using a delimiter — comma, space, newline, or any string. Great for parsing command arguments, CSV-style data, or multi-line input.

Base Set
{{parts}}{{parts.length}}{{parts.0}}
🔗
Join List
base.text.join

Joins list items into a single string with a separator between each item. Perfect for building comma-separated lists, bullet lines, or one big message from many sparks.

Base Set
{{joined}}
🔄
Replace Text
base.text.replace

Replaces every occurrence of a find string with another value. Handy for sanitizing input, swapping placeholders, or cleaning up user text.

Base Set
{{replaced}}
🔤
Transform Text
base.text.transform

Transforms text casing or whitespace. Use trim before comparing strings, uppercase for shouty replies, or title case for display names.

Base Set
{{transformed}}
📏
Text Length
base.text.length

Returns how many characters are in a piece of text. Useful for validating message length, word-count style games, or limits before sending.

Base Set
{{length}}
🔢
List Length
base.list.length

Returns how many items are in a list spark. Pair with Condition to branch on empty lists or check leaderboard size.

Base Set
{{count}}
🎯
Get List Item
base.list.get

Returns the item at a zero-based index — 0 is first, 1 is second, and so on. Great for showing the top scorer or a random pick you already stored.

Base Set
{{row}}
🍰
Slice List
base.list.slice

Returns a slice of a list from a start index up to (but not including) an end index. Use it for top-10 boards, paging, or trimming edges.

Base Set
{{slice}}{{slice.length}}{{slice.0}}
🧹
Unique List
base.list.unique

Deduplicates a list while keeping the first occurrence of each value. Handy after merges, user submissions, or tag lists.

Base Set
{{unique}}{{unique.length}}{{unique.0}}
🔍
List Contains
base.list.contains

Checks whether a list contains an item (string match). Routes to Found when present, Empty when not — like a mini condition for lists.

Base Set
foundempty
📡

Channels

1 Pieces
👤

User

4 Pieces
🛡️

Moderation

10 Pieces
👢
Kick Member
mod.kick

Removes a member from the server — they can still rejoin with an invite. A lighter touch than a ban; great for first offences or automated flows. Always pair with a Condition to confirm the boot is warranted.

The Mod Squad
outputerror
🔨
Ban Member
mod.ban

Permanently bans a user. Unlike Kick, they cannot rejoin. Optionally deletes their recent messages (1–7 days). Use with care — you can always Unban later if needed.

The Mod Squad
outputerror
🔓
Unban User
mod.unban

Removes a ban by user ID. They won't automatically rejoin — they'll need a fresh invite. Perfect for /unban commands where a mod reinstates a previously banned member.

The Mod Squad
outputerror
⏱️
Timeout Member
mod.timeout

Puts a member in Discord's native timeout. While timed out they can't send messages, add reactions, or join voice. Perfect for automated cool-downs. Requires Moderate Members permission. Max duration is 28 days.

The Mod Squad
outputerror
🔔
Remove Timeout
mod.untimeout

Clears an active timeout and restores a member's ability to chat. Handy for manual override commands — a mod can un-mute someone before their timeout would naturally expire.

The Mod Squad
outputerror
🗑️
Delete Message
mod.delete_message

Deletes one specific message from a channel. Use in message-event flows where bad content needs to go immediately. The message ID and channel ID are available as Sparks in message triggers.

The Mod Squad
outputerror
🧹
Purge Messages
mod.purge

Deletes up to 100 messages from a channel in one sweep. Discord only allows bulk-deleting messages under 14 days old. Perfect for cleaning up spam raids, accidental leaks, or bot noise.

The Mod Squad
outputerror
🐢
Set Slowmode
mod.slowmode

Sets a channel's slowmode cooldown — from 0 (off) to 21600 seconds (6 hours). Users can only send one message per cooldown. Great for calming heated chats, preventing raid spam, or managing high-traffic events.

The Mod Squad
outputerror
🔒
Lock Channel
mod.lock_channel

Removes @everyone's Send Messages permission in a channel — effectively locking it. The channel remains visible and readable; nobody can post until you Unlock it. Great for announcements-only mode or emergency lockdowns.

The Mod Squad
outputerror
🟢
Unlock Channel
mod.unlock_channel

Re-enables @everyone's Send Messages permission, reversing a Lock Channel. When the dust settles, unlock and get the conversation flowing again.

The Mod Squad
outputerror
🎛️

Controls

12 Pieces
🔘
Add Button
ui.button

Adds a button to whatever message you send next — Reply, Embed, Send or Follow-up all pick it up. Give it a custom id, then make a Button Pressed trigger with the same id to run a flow when someone clicks it. Five buttons fit on a row; after that they wrap.

The Control Panel
outputerror
➕
New Button Row
ui.row

Buttons pack five to a row on their own. Drop this between two Add Button pieces when you want the layout broken up sooner — handy for grouping actions.

The Control Panel
🔽
Add Dropdown
ui.select

Adds a dropdown to the next message, with choices you type in. Pair it with a Dropdown Chosen trigger to run a flow when someone picks — what they picked arrives as {{component.value}}.

The Control Panel
outputerror
👤
Add Member Picker
ui.select.user

Adds a dropdown Discord fills with members of the server — no choices to write. The chosen member's ID comes back as {{component.value}}.

The Control Panel
outputerror
🎭
Add Role Picker
ui.select.role

Adds a dropdown Discord fills with the server's roles. The chosen role ID comes back as {{component.value}} — perfect for self-serve role menus.

The Control Panel
outputerror
#️⃣
Add Channel Picker
ui.select.channel

Adds a dropdown Discord fills with the server's channels, optionally narrowed to one kind. The chosen channel ID comes back as {{component.value}}.

The Control Panel
outputerror
📣
Add Mention Picker
ui.select.mentionable

Adds a dropdown listing both members and roles, for when either answer would do. The chosen ID comes back as {{component.value}}.

The Control Panel
outputerror
✏️
Add Form Field
ui.text_input

Adds one input box to the pop-up form you open next. Put one of these before Show Pop-up Form for each thing you want to ask. Whatever gets typed comes back to your Form Submitted trigger as {{form.values.<custom id>}}.

The Control Panel
outputerror
🪟
Show Pop-up Form
ui.modal

Opens a pop-up form built from the Add Form Field pieces before it. This has to be the very first thing a flow does — Discord won't show a form after the bot has already replied, so don't put a Reply, a Wait, or any slow piece ahead of it. Catch the answers with a Form Submitted trigger.

The Control Panel
outputerror
♻️
Update the Message
ui.update

Rewrites the message the button or dropdown lives on, instead of posting a new one. Stage new buttons before this piece to swap them out; stage none and the existing ones stay put. The classic 'you voted!' in-place update.

The Control Panel
outputerror
🚫
Grey Out the Buttons
ui.disable

Leaves every button and dropdown on the message visible but dead. The one-line fix for people clicking twice, or voting after the poll closed.

The Control Panel
outputerror
🧽
Remove the Buttons
ui.clear

Takes every button and dropdown off the message, leaving the text behind. Good for one-shot prompts that shouldn't linger.

The Control Panel
outputerror
🖼️

Images

10 Pieces
👤
Grab Avatar
img.avatar

Fetches a member's avatar as an image spark you can drop into the Studio, touch up, or attach. Leave Whose avatar empty for whoever triggered the flow — or whoever got right-clicked. Animated avatars stay animated unless you freeze them.

The Darkroom
outputerror
🏰
Grab Server Art
img.server_icon

Fetches this server's icon, banner or invite splash as an image spark. Works on Interaction Only bots too — Kitcord looks the server up on demand.

The Darkroom
outputerror
📎
Grab Images from a Message
img.from_message

Collects the images on a message — uploads, embed images and stickers. Use the message that triggered the flow, the one that was right-clicked, the one it replies to, or any message link. You get the first (or last) image plus a list of all of them.

The Darkroom
foundempty
🔗
Load Image from a Link
img.from_url

Downloads a PNG, JPEG, GIF or WebP from any https link — handy for APIs that return image URLs. Links to private networks are blocked, and files over 10 MB are refused.

The Darkroom
outputerror
🖼️
Pull from Gallery
img.gallery

Fetches an image from this bot's Gallery tab — by its label, or by tag (a random one, the newest, the oldest, or all of them). Upload new images any time; flows see them instantly, no republish needed.

The Darkroom
foundempty
🔍
Read an Image
img.inspect

Looks at an image and tells your flow about it: width, height, format, whether it's animated, its dominant colour, a 5-colour palette and how bright it is. Branches on whether it's an image at all — perfect for validating uploads.

The Darkroom
imagenot_image
🎨
Develop in Studio
img.studio

Opens the Studio: lay out avatars, text, shapes and progress bars on a canvas, drop in sparks like {{user.displayName}} or {{vars.xp}}, and the bot develops a fresh image every run. If an avatar or Gallery image in the design is animated, the result is an animated GIF.

The Darkroom
outputerror
✂️
Touch Up
img.edit

Runs a stack of quick edits on an image, top to bottom: resize, crop to a circle, rotate, adjust colours, add a border, slap on meme captions, deep-fry it, or speed up / reverse / boomerang a GIF. Animated GIFs stay animated.

The Darkroom
outputerror
🎞️
Flipbook
img.gif

Stitches up to 30 images into an animated GIF, one after another. Feed it a list spark (like every image on a message, or every Gallery image with a tag), or one image per line.

The Darkroom
outputerror
📤
Attach Image
img.attach

Attaches an image to whatever message you send next — Reply, Embed, Follow-up, Send, DM, or Update the Message all pick it up, just like buttons do. Add alt text for screen readers, or hide it behind a spoiler.

The Darkroom
outputerror