Skip to main content
OpenWorkflow supports the Standard Schema spec, which means you can use your preferred validation library to enforce type-safe contracts on workflow inputs. If the input doesn’t match the schema, the error is thrown before the workflow is even enqueued β€” preventing invalid data from entering your system.

Overview

When you define a workflow, you can provide a schema. OpenWorkflow uses this schema to:
  1. Validate inputs at runtime: When you start a run (for example, with ow.runWorkflow(workflow.spec, input)), the input is validated immediately. If validation fails, an error is thrown before the workflow is enqueued, preventing invalid data from entering your system.
  2. Type Safety: The input parameter in your workflow function is automatically typed based on your schema.

Supported Libraries

Any library that implements the Standard Schema specification works out of the box, including: And many more.

Examples

Zod

import { defineWorkflow } from "openworkflow";
import { z } from "zod";

const emailSchema = z.object({
  to: z.string().email(),
  subject: z.string().min(1),
  body: z.string(),
});

export const sendEmail = defineWorkflow(
  {
    name: "send-email",
    schema: emailSchema,
  },
  async ({ input, step }) => {
    // `input` is fully typed as { to: string; subject: string; body: string }

    await step.run({ name: "send-email" }, async () => {
      await emailProvider.send({
        to: input.to,
        subject: input.subject,
        body: input.body,
      });
    });

    return { success: true, recipient: input.to };
  },
);

Valibot

import { defineWorkflow } from "openworkflow";
import * as v from "valibot";

const emailSchema = v.object({
  to: v.pipe(v.string(), v.email()),
  subject: v.pipe(v.string(), v.minLength(1)),
  body: v.string(),
});

export const sendEmail = defineWorkflow(
  {
    name: "send-email",
    schema: emailSchema,
  },
  async ({ input, step }) => {
    // `input` is fully typed as { to: string; subject: string; body: string }

    await step.run({ name: "send-email" }, async () => {
      await emailProvider.send({
        to: input.to,
        subject: input.subject,
        body: input.body,
      });
    });

    return { success: true, recipient: input.to };
  },
);

ArkType

import { type } from "arktype";
import { defineWorkflow } from "openworkflow";

const emailSchema = type({
  to: "string.email",
  subject: "string>0",
  body: "string",
});

export const sendEmail = defineWorkflow(
  {
    name: "send-email",
    schema: emailSchema,
  },
  async ({ input, step }) => {
    // `input` is fully typed as { to: string; subject: string; body: string }

    await step.run({ name: "send-email" }, async () => {
      await emailProvider.send({
        to: input.to,
        subject: input.subject,
        body: input.body,
      });
    });

    return { success: true, recipient: input.to };
  },
);

Validation Errors

When validation fails, OpenWorkflow throws a detailed error that includes information about what went wrong:
import { ow } from "./openworkflow/client";
import { sendEmail } from "./workflows/send-email";

try {
  await ow.runWorkflow(sendEmail.spec, {
    to: "invalid-email", // Invalid email format
    subject: "", // Too short
    body: "Hello",
  });
} catch (error) {
  console.error("Validation failed:", error);
}