Build a workflow
An endpoint answers a question. A workflow watches for something and acts — which is where execution guarantees start to matter.
Anatomy
import { defineWorkflow } from "@ripar/sdk";
export default defineWorkflow({
name: "liquidation-guard",
trigger: { type: "cron", every: "5m" },
async run({ step, ctx }) {
const health = await step("check", () =>
ctx.call("folks/health", { address: ctx.env.WALLET })
);
if (health.ratio >= 1.4) return { skipped: true };
// Each `step` is checkpointed: a retry resumes here rather than
// re-running `check` and paying for it twice.
const tx = await step("top-up", () =>
ctx.call("folks/supply", { amount: health.deficit })
);
return { toppedUp: true, tx };
},
});Triggers
| Trigger | Fires when | Example |
|---|---|---|
cron | On a schedule | { type: "cron", every: "5m" } |
webhook | An HTTP call arrives | { type: "webhook" } |
onchain | An Algorand event matches | { type: "onchain", app: 1234, event: "Swap" } |
manual | You invoke it | ripar run liquidation-guard |
Execution guarantees
This is the part that separates a workflow from a cron job that calls curl.
- Retries with exponential backoff — transient failures are retried; the schedule is jittered so a downstream outage does not get a thundering herd.
- Step checkpointing — completed steps are not re-executed on retry. You do not pay twice for the same paid call.
- Idempotency keys — carried automatically into every
ctx.call. - Nonce-safe submission — concurrent onchain actions from one workflow are serialised so they cannot collide.
Wrap every paid call in a step
A ctx.call outside a step() is re-executed on retry — and re-paid. Steps are how
you make retries free.
Conditions and branching
const price = await step("price", () => ctx.call("algo-usd/spot"));
if (price.value < ctx.env.FLOOR) {
await step("alert", () => ctx.call("notify/slack", { text: "below floor" }));
return { acted: "alerted" };
}Plain control flow. There is no DSL to learn.
Budgets
Cap what a workflow can spend before it can spend it:
budget: { perRun: "0.25", perDay: "5.00" },Exceeding the cap fails the run rather than the wallet. See Security.
Watching runs
ripar runs liquidation-guard --last 20
ripar runs show run_8c21Every attempt, its steps, its payments and its result are in the record — including the attempts that failed.