Skip to content
Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →

Defining Functions

A function is a workflow definition that responds to events. Functions contain one or more steps that execute in sequence, with automatic memoization for durability.

import { ironflow } from "@ironflow/node";
export const processOrder = ironflow.createFunction(
{
id: "process-order",
triggers: [{ event: "order.placed" }],
},
async ({ event, step }) => {
// Event data is typed via generics or casting
const { orderId } = event.data as { orderId: string };
const result = await step.run("validate", async () => {
return await validateOrder(orderId);
});
return { success: true, result };
},
);

Functions accept a configuration object to tune retry logic, timeouts, and concurrency. The concurrency.key field is a dot-notation path into the triggering event’s data payload (e.g. orgId, or org.id for a nested field). A key that does not resolve to a scalar fails the run with CONCURRENCY_KEY_UNRESOLVED.

These are the common ones. For every field — including stepTimeout, recording, recordingProfile, actorKey, name, description and metadata — see FunctionConfig. Two fields have their own pages: debounce and cancelOn.

ironflow.createFunction(
{
id: "my-function",
triggers: [{ event: "my.event" }],
mode: "push", // "push" (default) or "pull"
// Push execution budget in ms. It can only *tighten* the server's
// `engine.pushTimeout` ceiling (default 10s), never raise it, and it is not
// applied at all in pull mode (which is unlimited).
timeout: 5_000,
retry: {
maxAttempts: 3,
initialDelayMs: 1000,
backoffFactor: 2.0,
},
concurrency: {
limit: 10,
key: "orgId", // Group limits by this field of event.data
},
secrets: ["STRIPE_KEY"], // Inject secrets at runtime
},
handler,
);

The event object in the context contains the triggering event’s payload and metadata.

import { z } from "zod";
// Define your event schema with Zod
const orderPlacedSchema = z.object({
orderId: z.string(),
amount: z.number(),
});
// Pass the schema in the config for type-safe event data
export const myFn = ironflow.createFunction(
{
id: "my-fn",
triggers: [{ event: "order.placed" }],
schema: orderPlacedSchema,
},
async ({ event, step }) => {
// event.data is now type-safe: { orderId: string, amount: number }
console.log(event.data.orderId);
// Access user-defined metadata attached when the event was emitted
const traceId = event.metadata?.traceId as string | undefined;
const source = event.metadata?.source as string | undefined;
},
);

The schema is enforced at runtime on every execution path (push, pull, streaming pull, and the test harness). Before the handler runs, the SDK parses event.data with the schema. On success, the handler receives the parsed value, so Zod defaults and transforms apply and z.object() strips keys the schema does not declare (use .loose() to keep them). On failure, the run fails with a non-retryable SchemaValidationError (code VALIDATION_ERROR) that names the function and the first ten Zod issues; issue text can include received values. The event record is kept, so you can inspect the payload in the dashboard. Cron ticks are not validated: their payload is engine-generated, so a function with both an event and a cron trigger keeps running on schedule. Functions without a schema receive event.data unchanged.


If your function requires sensitive API keys, declare them in the secrets array. Ironflow will resolve these from its encrypted vault and inject them into the handler context at runtime.

async ({ event, step, secrets }) => {
const apiKey = secrets.get("STRIPE_KEY");
};