Loops & dynamic workflows
Stop prompting. Start building loops. A loop is a chain of automated steps that runs on a schedule, webhook, or trigger. You design the routine once — PromptFloe does the work forever.
#The mental model
Most tools require a human in the loop — you prompt, it responds, you review, you prompt again. PromptFloe Loops invert this. Your job is to design the loop. PromptFloe's job is to run it.
#Anatomy of a loop
Every loop has four parts:
name + descriptionHuman-readable identifier. Shows in the loop list and Slack notifications.triggerWhen to fire. Manual, cron schedule, or inbound webhook.steps[]An ordered chain. Each step has a type, a config, and a nextStepId. Condition steps have yesStepId + noStepId instead.maxIterationsHow many times the loop can cycle back to the first step via a condition. Prevents runaway loops.#Step types
Six step types cover every building block you need. Mix them freely in a single loop.
generateBuild or edit a full-stack app — frontend, backend, tests, all wired up.
Use when: Kick off a fresh app each iteration, or edit an existing one based on upstream LLM output.
appId, fileCount, frameworkllmRun any text or structured JSON task — summarise, classify, extract, rewrite.
Use when: Pre-process data before a generate step, or post-process results before a notify step.
result (string or JSON if schema provided)conditionPromptFloe evaluates a yes/no question and routes to yesStepId or noStepId.
Use when: Branch your loop based on generated content, webhook data, or LLM output.
answer (yes|no), reasoning, nextStepIdnotifySend a message to Slack, Telegram, or any webhook URL.
Use when: Alert your team when an app is generated, a condition is met, or a weekly report is ready.
sent: truedelayWait N seconds before the next step.
Use when: Give a deploy time to warm up, or space out API calls.
waited (seconds)webhookPOST (or GET/PUT/PATCH) to any external HTTP endpoint.
Use when: Trigger a GitHub Action, notify a third-party API, or push data to a database.
status, body (parsed response)#Memory & template interpolation
Every time a step completes, its output is added to the loop memory — a running key-value store keyed by step ID. Downstream steps can reference those values with {{stepId.field}}.
// After step "summarise" (llm):
memory = {
__trigger: { firedAt: "2026-06-06T09:00:00Z" },
summarise: { result: { text: "3 PRs need review: fix-auth, add-dark-mode, perf-opt" } }
}
// After step "build" (generate):
memory = {
...above,
build: { appId: "app_01xyz", fileCount: 34, framework: "next" }
}
// After step "notify" (notify):
memory = {
...above,
notify: { sent: true }
}| Field | Type | Description |
|---|---|---|
| {{stepId.field}} | — | Access a specific field from a previous step's output. E.g. {{build.appId}} |
| {{stepId.result.key}} | — | Access nested JSON from an llm step that used a schema. E.g. {{summarise.result.score}} |
| {{__trigger}} | — | The trigger payload — webhook body, cron firedAt timestamp, or manual input. |
| {{__trigger.body.userId}} | — | Drill into a webhook payload field. Useful for routing builds per-user. |
#Triggers
POST /v1/loops/:id/run — optionally pass a JSON body as trigger data."0 9 * * 1-5" = Mon–Fri at 9am. Timezone defaults to UTC but can be set to any IANA zone.POST /v1/loops/webhook/:token — no auth header needed. The body becomes {{__trigger}} in memory.#Condition branching
A condition step gives your loop a decision point. You write a yes/no question; PromptFloe evaluates it in fast mode by default — low cost, near-instant. The loop routes to yesStepId or noStepId based on the result.
You can also use conditions to loop back — set yesStepId to the first step's ID. The loop iterates up to maxIterations times, then stops automatically.
#Real-world examples
Four patterns covering the most common use cases. Each one maps to a loop you can build in under 5 minutes.
webhookFetch open PRsllmSummarise PRsnotifyPost to SlackllmCheck content freshnessconditionIs page outdated?generateRebuild landing pagewebhookTrigger Vercel deploynotifySlack confirmationllmParse incoming payloadgenerateBuild the appwebhookRegister in your CMSnotifyNotify requesterllmAudit current appconditionAre improvements needed?generateApply improvementsnotifyReport changes#Creating your first loop
Open the loop builder
Go to /loops in the sidebar and click New loop. Or navigate directly to /loops/new.
Name it and set a trigger
Give your loop a clear name (e.g. "Daily PR summary") and pick a trigger:
- Manual — good while you're testing
- Cron — enter a cron expression like
0 9 * * 1-5 - Webhook — the server generates a secret token on save
Add steps
Click the step type buttons at the bottom of the builder. For each step:
- Give it a name — this is what shows in run history and template refs
- Fill in the required fields for its type
- Reference prior steps with
{{stepName.field}}in any text field
Set maxIterations
If your loop can cycle back (a condition step routes to the first step), set maxIterations to limit how many cycles per run. For a linear chain with no loop-back, leave it at 1.
Save and run
Click Create loop. You'll land on the detail page. Hit Run now to fire your first execution and watch the step records appear in real time.
#Using the API
All loop operations are available as REST endpoints. Authenticate with a JWT session token or a pf_... API key.
| Field | Type | Description |
|---|---|---|
| POST /v1/loops | — | Create a loop. Returns the full loop definition including webhook token if applicable. |
| GET /v1/loops?workspaceId= | — | List all loops for a workspace. |
| PATCH /v1/loops/:id | — | Update steps, trigger, or enabled flag. Cron schedule auto-syncs. |
| DELETE /v1/loops/:id | — | Delete a loop and all its run history. |
| POST /v1/loops/:id/run | — | Manually fire a run. Optional JSON body becomes {{__trigger}}. |
| GET /v1/loops/:id/runs | — | List last N runs (default 20). |
| GET /v1/loops/:id/runs/:runId | — | Run detail + all step records with duration, output, and errors. |
| DELETE /v1/loops/:id/runs/:runId | — | Cancel an in-progress run. |
| POST /v1/loops/webhook/:token | — | Public webhook trigger — no auth header needed. Body becomes trigger data. |
# Trigger a loop from a GitHub Action after a deploy
curl -X POST https://api.promptfloe.com/v1/loops/:id/run \
-H "Authorization: Bearer pf_..." \
-H "Content-Type: application/json" \
-d '{"deployedAt":"2026-06-06T12:00:00Z","env":"production","version":"1.4.2"}'
# Or use the public webhook endpoint (no auth header)
curl -X POST https://api.promptfloe.com/v1/loops/webhook/your-secret-token \
-d '{"event":"deploy.success","sha":"abc123"}'#Run history & observability
Every run is tracked end-to-end. In the loop detail page (/loops/:id) you can:
- See the status of every run (running, completed, failed, cancelled)
- Expand any run to see each step record — type, duration, output, and error if it failed
- Cancel a run that's still in progress
- The page auto-refreshes every 4 seconds when a run is active
#Tips & limits
| Field | Type | Description |
|---|---|---|
| Max steps per loop | — | 50 steps. |
| Max iterations per run | — | 1000 (default 1). Set higher only for controlled loops with a clear termination condition. |
| Generate step timeout | — | 8 minutes. If the generation hasn't completed, the step fails and the run is marked failed. |
| Delay step cap | — | Capped at 30 minutes (1800s) regardless of what you set. |
| Webhook step timeout | — | 30 seconds per webhook call. |
| Template refs | — | Only previous steps' outputs are in memory — you can't reference a step that hasn't run yet. |
| Speed tier for conditions | — | Always fast mode — low cost, near-instant. The condition only answers yes/no. |
| Concurrency | — | Up to 10 steps run concurrently across all your loops (not per loop — sequential within one run). |
Send loop notifications via Slack, Telegram, or Teams.
Generate a pf_... key to trigger loops from CI/CD.
Open the loop builder and create your first routine.