Skip to main content
The Kestrel Python SDK provides a typed, fluent API for building and managing workflows programmatically. It supports both synchronous and asynchronous usage with API key authentication.

Installation

Authentication

Create an API key in the Kestrel platform under Workflows > API Keys, then use it to authenticate:
For async usage:
If you’re logged in via the CLI, the SDK can reuse your session:

Workflow Builder

Build workflows using typed classes with full IDE autocompletion:

Triggers

Create triggers for different signal sources:
Each request trigger can be scoped further:

Submitting Requests Programmatically

The async client mirrors this via client.requests.confirm(request_id, confirm).

Custom Webhook

Trigger.custom_webhook(“my-event-type”)

Datadog Monitor

Trigger.datadog_monitor_alert() Trigger.datadog_monitor_warn() Trigger.datadog_monitor_recovered() Trigger.datadog_monitor_no_data() Trigger.datadog_monitor_any() Trigger.datadog_monitor_alert().monitor_names(“High CPU”).datadog_tags(“env:production”)
Fly.io triggers are poll-based — set the apps, regions, and poll cadence to control how often Kestrel checks the Fly Machines API:
Nebius triggers are also poll-based — scope to projects/clusters and set the poll cadence to control how often Kestrel checks the Nebius compute and Managed Kubernetes APIs:
Jenkins and CircleCI triggers are webhook-based — scope to jobs/projects and optionally statuses:
Terraform Cloud triggers are webhook-based — scope to workspaces and optionally run statuses:
Pulumi Cloud triggers are webhook-based — scope to stacks (project/stack) and optionally projects:

Actions

Every integration has typed factory methods:

Conditions (Branching)

Available operators: equals, not_equals, contains, not_contains, exists, not_exists. The value-comparing factories accept multiple candidate values — equals/contains are met when the field matches any value, not_equals/not_contains only when it matches none:

Poll Until (Loops)

PollUntil builds a self-looping node that repeatedly executes ONE embedded catalog action at a fixed interval and exits on the met branch when the condition holds against that iteration’s output, or on the timeout branch when the timeout elapses. Wire the branches with .on_met() / .on_timeout():
  • .every(seconds=..., minutes=...) — polling interval (default 60s, minimum 30s).
  • .timeout(minutes=..., hours=...) — loop timeout (default 60 minutes).
  • Downstream steps on the met branch can reference the final iteration’s output fields via {{step_outputs.<loop-id>.<field>}}.
Polling is durable across server restarts: an in-flight interval resumes its countdown rather than restarting.

For Each (Fan-Out)

ForEach builds a fan-out node that resolves a list from an upstream step’s output at runtime and executes ONE embedded catalog action per element, sequentially. Per-item config templates may reference {{item}} (the current element), {{item.<field>}} (a field of an object element), and {{item_index}} (the zero-based index):
  • The items expression must resolve to a list: an array output, a JSON-encoded array string, or a comma-separated string.
  • .max_items(n) — caps the fan-out (server default 25, hard cap 100).
  • .continue_on_error() — a failed item doesn’t fail the node; remaining items still run.
  • The node emits one aggregated output (items_total, items_processed, succeeded, failed, per-item results, and a flat summary), referenced as {{step_outputs.<foreach-id>.<field>}}.
  • ForEach runs items once each, immediately. To repeatedly re-run an action until a condition holds, use PollUntil instead.

Approvals

Types: Approval.manual(), Approval.slack(channel), Approval.pr_approval(), Approval.pr_merge().

Iterative RCA Refinement (human-in-the-loop)

Approval.refine() builds a single self-looping gate that presents the upstream RCA + fixes for review. When the approver requests changes with free-text guidance, the same upstream RCA agent re-runs with that feedback (accumulated across rounds) and re-requests approval — looping until approved/rejected or max_rounds is reached. It must be placed after a kestrel_trigger_rca() or kestrel_trigger_cloud_rca() step.
Use Approval.refine("slack") to request the refinement approval in Slack instead of the Kestrel UI. max_rounds defaults to 5; once exhausted the loop advances on the approved branch.

Workflow Management

List, Get, Update, Delete

Generate from Natural Language

Execution Management

List and Inspect Executions

Test and Wait

Trigger a test execution and wait for it to complete:

Async SDK

The AsyncKestrelClient provides the same API surface with async/await:

Approvals

Version History & Rollback

Execution Replay

Replay failed executions from the beginning or from the failed step:

Error Handling

API Key Scopes

API keys can be scoped to specific permissions: Create scoped keys in the platform under Workflows > API Keys.