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:

CheckUse it for
has role / missing rolestaff-only branches
in channel / not in channelnarrow a rule further
variable comparison=, , contains, exists, and >, , <,
random chancerun 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.

flav-codeWritten on 8 August 2026