If icon
Fail icon
List icon
ForEach icon
Delete icon
DiscordIncomingWebhook icon
Schedule icon

Stale Miro Board Cleanup with a Confirmation Gate

Sweep a Miro team for boards untouched for N days with Kestra and delete them behind an explicit confirmation gate, with a Discord audit summary.

Categories
Business

Old boards pile up until finding the current one means scrolling past two years of abandoned brainstorms, and every abandoned board is also an unwatched surface still sharing whatever was pasted onto it. This blueprint automates the cleanup with the caution deletion deserves. The flow lists a team's boards with io.kestra.plugin.miro.boards.List, compares each board's modifiedAt timestamp against a configurable staleness cutoff, and deletes the stale ones with io.kestra.plugin.miro.boards.Delete. Because Miro deletion is permanent and cannot be undone, an If guard fails the execution before anything is listed unless the confirm_delete input is explicitly true, and a Discord summary records every sweep.

How it works

  1. confirm_gate (io.kestra.plugin.core.flow.If) checks the confirm_delete boolean first. When false, the nested io.kestra.plugin.core.execution.Fail task stops the execution with a clear message before any Miro call is made.
  2. list_boards (io.kestra.plugin.miro.boards.List) fetches up to 50 boards for the team, exposing the page as {{ outputs.list_boards.boards }} plus total and size scalars.
  3. sweep_stale (io.kestra.plugin.core.flow.ForEach) iterates the board objects; inside the loop each board arrives as a JSON string, decoded with fromJson(taskrun.value).
  4. check_stale compares modifiedAt against now() minus stale_days. Both sides are rendered as yyyy-MM-dd strings, which sort correctly under plain string comparison, so the date logic stays one readable line.
  5. delete_board runs only inside the If branch and reads the board ID with fromJson(parent.taskrun.value).id, since tasks nested under an If inside a ForEach reach the loop value through parent.
  6. notify posts a Discord audit summary with the scanned counts and threshold; the errors block reports stopped or failed sweeps, including unconfirmed runs.
  7. A disabled-by-default quarterly Schedule trigger is included; scheduled runs use input defaults, so confirm_delete defaulting to false keeps even an accidentally enabled schedule harmless.

What you get

  • A workspace where the board list reflects current work, swept on your threshold, not by hand.
  • A hard confirmation gate, so the destructive path requires a deliberate true and fails safe otherwise.
  • Per-board delete results in the execution tree, an audit trail of exactly what was removed and when.
  • A Discord record of every sweep for whoever asks where a board went.

Who it's for

  • Workspace admins whose Miro team has accumulated years of one-off boards.
  • IT teams reducing the number of forgotten surfaces that still share company content.
  • Ops engineers who want lifecycle policies for collaboration tools, not just for infrastructure.

Why orchestrate this with Kestra

A cleanup script with a delete call in a loop is exactly the kind of automation that needs guardrails: typed inputs with safe defaults, a fail-fast confirmation gate, per-iteration visibility into what was deleted, and a permanent execution history. Kestra provides all of that declaratively, and the same flow serves as both the quarterly job and the documented, repeatable manual procedure.

Prerequisites

  • A Miro OAuth access token with boards:read and boards:write scopes.
  • The Miro team ID to sweep.
  • A Discord incoming webhook for audit summaries.

Secrets

  • MIRO_ACCESS_TOKEN: Miro OAuth 2.0 access token.
  • DISCORD_WEBHOOK_URL: Discord incoming webhook URL.

Quick start

  1. Add the two secrets to your Kestra namespace.
  2. Execute the flow with confirm_delete left false and confirm it fails fast with the guard message.
  3. Create a throwaway team or use a test token scope, run with confirm_delete true and a high stale_days, and verify the Discord summary and per-board results.
  4. Only then consider enabling the quarterly trigger.

How to extend

  • Sweep more than 50 boards by paging with the task's offset property across repeated runs.
  • Replace deletion with a softer action, a boards.Update renaming stale boards with an ARCHIVE prefix, keeping the same loop and gate.
  • Post the would-be deletions to Discord first and require a second execution to delete, a two-phase dry run.
  • Exclude protected boards by filtering on a name convention with the List task's query property.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.