Conditions and timing
Conditions on a step
Any step can carry up to 5 conditions. Choose whether all of them or any of them must hold, and what happens when they do not: skip just this step, or stop the workflow.
Available checks:
| Check | Use it for |
|---|---|
| has role / missing role | staff-only branches |
| in channel / not in channel | narrow a rule further |
| variable comparison | =, ≠, contains, exists, and >, ≥, <, ≤ |
| random chance | run a step only N% of the time |
The numeric comparisons are what make counters useful:
2. Add 1 to the strike counter → {steps.2.value}
3. Warn the member
4. Time them out only if {steps.2.value} ≥ 3
5. Reset the counter only if {steps.2.value} ≥ 3
There is no if/else block. You get the same result with two steps carrying
opposite conditions (≥ 3 and < 3), which stays readable in a flat list.
A condition that does not hold is a normal outcome, not an error: the run is still a success.
Waiting
Three flow actions:
- Wait: pause up to 60 seconds inside the run
- Wait, cancellable: park everything below it for up to 30 days under a key you choose
- Cancel: cancel whatever is parked under that key
The cancellable wait is durable: the schedule lives in the database, not in memory, so a restart in the middle changes nothing.
The key is what makes it safe. Use honeypot:{user.id} and each member's
timer is separate: cancelling only ever affects your own.
That pattern is the whole "second chance" idea:
1. Delete the message
2. Post a card with an "it was a mistake" button
3. Wait 5 minutes, cancellable, key honeypot:{user.id}
4. Ban ← only reached if nobody cancelled
A second rule on that button cancels the same key. Click in time and step 4 never happens.
What survives the wait
A cancellable wait ends the run and starts a fresh one when the timer fires, so
what the second half can see is decided at the moment you park it. Carried
over: your trigger variables ({user.mention}, {server.name}, …), anything
you stored in a {vars.…} variable, the user, channel and message the rule
fired on, and the {steps.N.field} results of the earlier steps that your
parked steps actually read. So this works:
1. Send message → {steps.1.message_id}
2. Wait 5 minutes, cancellable
3. Edit message {steps.1.message_id} ← still resolves
Only what the steps below the wait mention is kept, so nothing you do not use
is stored. If those results add up to more than 16 KB the step refuses to park
with deferred_state_too_large instead of quietly dropping half of them; the
realistic way to hit that is a large HTTP response, and the fix is to pull out
the one field you need first:
1. Call HTTP endpoint into {vars.api}
2. Set variable id = {vars.api.data.id}
3. Wait 1 hour, cancellable
4. Send message using {vars.id}
Two things are deliberately not carried. The interaction token, because Discord expires it after 15 minutes, so a reply to the click that started the rule cannot work an hour later. And the member's role list, because a snapshot taken an hour ago could make a moderation step act on roles they no longer have.
Steps after the wait hand values to each other normally: they all run in the same second pass.
