Documentation Index
Fetch the complete documentation index at: https://openworkflow.dev/llms.txt
Use this file to discover all available pages before exploring further.
OpenWorkflow is built with TypeScript and provides full type inference for
workflow inputs, outputs, and step results. You get autocomplete and compile-
time checking throughout.
Schema Validation with Type Inference
For runtime validation with type inference, use Standard
Schema compatible libraries like Zod:
import { z } from "zod";
const inputSchema = z.object({
email: z.string().email(),
amount: z.number().positive(),
});
export const payment = defineWorkflow(
{
name: "payment",
schema: inputSchema,
},
async ({ input, step }) => {
// input is typed as { email: string; amount: number }
// and validated at runtime
},
);
Explicit Type Definitions
To manually specify input and (optionally) output types, use generics with
defineWorkflow:
import { defineWorkflow } from "openworkflow";
interface OrderInput {
orderId: string;
customerId: string;
}
interface OrderResult {
success: boolean;
trackingNumber: string;
}
export const processOrder = defineWorkflow<OrderInput, OrderResult>(
{ name: "process-order" },
async ({ input, step }) => {
const validated = await step.run({ name: "validate" }, async () => {
// input is typed as OrderInput
return validateOrder(input.orderId);
});
const trackingNumber = await step.run({ name: "ship" }, async () => {
return shipOrder(input.orderId);
});
// Return type must match OrderResult
return {
success: true,
trackingNumber,
};
},
);
If you only specify the input type, the output type is inferred from the return
value:
export const greet = defineWorkflow<{ name: string }>(
{ name: "greet" },
async ({ input }) => {
return `Hello, ${input.name}!`; // Output inferred as string
},
);
Workflow Specs
When using defineWorkflowSpec for separate declaration and implementation,
types flow through:
import { defineWorkflowSpec, OpenWorkflow } from "openworkflow";
// Declare the workflow spec with types
export const emailSpec = defineWorkflowSpec<
{ to: string; subject: string },
{ sent: boolean }
>({
name: "send-email",
});
// Implementation receives typed input
const ow = new OpenWorkflow({ backend });
ow.implementWorkflow(emailSpec, async ({ input, step }) => {
// input.to and input.subject are typed
await step.run({ name: "send" }, async () => {
await sendEmail(input.to, input.subject);
});
return { sent: true };
});
Client-Side Type Safety
When you run a workflow, the handle is typed with the output:
const handle = await ow.runWorkflow(processOrder.spec, {
orderId: "123",
customerId: "456",
});
// result is typed as OrderResult
const result = await handle.result();
console.log(result.trackingNumber); // TypeScript knows this exists
Step Result Types
Step return types are inferred automatically:
const workflow = defineWorkflow<{ userId: string }>(
{ name: "user-workflow" },
async ({ input, step }) => {
// user is inferred as User type from the return
const user = await step.run({ name: "get-user" }, async () => {
const response = await fetch(`/api/users/${input.userId}`);
return response.json() as User;
});
// TypeScript knows user has these properties
console.log(user.email, user.name);
},
);