New to Kestra?
Use blueprints to kickstart your first workflows.
Build a dynamic resource request workflow in Kestra with conditional inputs, a Slack approval message, and a human-in-the-loop pause that resumes on demand.
Turn ad hoc resource requests into a governed, auditable workflow. This blueprint shows how to combine Kestra conditional inputs, a Slack approval notification, and a human-in-the-loop pause so that access permissions, SaaS applications, development tools, and cloud VMs are only provisioned after a reviewer signs off. The form adapts to what the requester picks, the approver gets a one-click Slack link, and every decision is captured in the execution history for compliance and IT service management.
resource_type (Access permissions, SaaS application, Development tool, or Cloud VM). Using the dependsOn property with a condition, only the relevant follow-up inputs appear: access_permissions, saas_applications, development_tools, or the cloud_provider, cloud_vms, and region chain. Option lists are loaded dynamically from the KV store via {{ kv(...) }} expressions.send_approval_request task (io.kestra.plugin.slack.notifications.SlackIncomingWebhook) posts an approval message to Slack containing a Resume link back to the execution.wait_for_approval task (io.kestra.plugin.core.flow.Pause) halts the execution with onResume fields approved (BOOL) and comment (STRING), waiting for a human decision.approve task (io.kestra.plugin.core.http.Request) POSTs the collected {{ inputs }} to a downstream provisioning endpoint.log task (io.kestra.plugin.core.log.Log) records the approval comment and the response body for traceability.Slack and KV lists alone cannot pause a process, wait for a human, and resume exactly where it left off. Kestra adds event-driven and API-triggered execution, a durable Pause with typed onResume inputs, retries on the Slack and HTTP calls, full execution lineage, and everything defined declaratively in YAML. The dynamic dependsOn inputs build a smart form that no chat tool or static webhook can replicate.
This blueprint uses a mock webhook URL inline for demonstration. In production, replace the url on send_approval_request with your real Slack incoming webhook, ideally stored as a secret and referenced with {{ secret('SLACK_WEBHOOK_URL') }}.
Add the required KV pairs (access_permissions, saas_applications, development_tools, cloud_vms, cloud_regions) used by the input expressions:
id: add_kv_pairs
namespace: company.team
tasks:
- id: access_permissions
type: io.kestra.plugin.core.kv.Set
key: "{{ task.id }}"
kvType: JSON
value: |
["Admin", "Developer", "Editor", "Launcher", "Viewer"]
- id: saas_applications
type: io.kestra.plugin.core.kv.Set
key: "{{ task.id }}"
kvType: JSON
value: |
["Slack", "Notion", "HubSpot", "GitHub", "Jira"]
- id: development_tools
type: io.kestra.plugin.core.kv.Set
key: "{{ task.id }}"
kvType: JSON
value: |
["Cursor", "IntelliJ IDEA", "PyCharm Professional", "Datagrip"]
- id: cloud_vms
type: io.kestra.plugin.core.kv.Set
key: "{{ task.id }}"
kvType: JSON
value: |
{
"AWS": ["t2.micro", "t2.small", "t2.medium", "t2.large"],
"GCP": ["f1-micro", "g1-small", "n1-standard-1", "n1-standard-2"],
"Azure": ["Standard_B1s", "Standard_B1ms", "Standard_B2s", "Standard_B2ms"]
}
- id: cloud_regions
type: io.kestra.plugin.core.kv.Set
key: "{{ task.id }}"
kvType: JSON
value: |
{
"AWS": ["us-east-1", "us-west-1", "us-west-2", "eu-west-1"],
"GCP": ["us-central1", "us-east1", "us-west1", "europe-west1"],
"Azure": ["eastus", "westus", "centralus", "northcentralus"]
}
Point send_approval_request at your Slack webhook (or its secret).
Run the flow, pick a resource_type, and submit the dynamic form.
Open the Slack message, click Resume, set approved and comment, and watch the flow finish.
approve URL for your real provisioning API (Terraform Cloud, AWS, GCP, or an ITSM system).approved with an If task to skip provisioning when rejected.