Skip to content

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.

Basic Function Definition

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 };
},
);

Configuration Options

Functions accept a configuration object to tune retry logic, timeouts, and concurrency. The concurrency.key field uses dot-notation paths starting from the event root (e.g. event.data.orgId).

These are the common ones. For every field — including stepTimeout, recording, recordingRetention, actorKey, name 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: "event.data.orgId", // Group limits by this field
},
secrets: ["STRIPE_KEY"], // Inject secrets at runtime
},
handler,
);

Event Data Access

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.


Secrets Injection

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");
};

What’s Next?