Standards Docs
Guides

Workflows

Declare and run multi-step workflows using FormDefinition and the blocks runtime.

Workflows are multi-step processes you declare in code. The runtime lives in @stndrds/blocks, powered by RilayKit.

Form definitions

A workflow is expressed as a FormDefinition — a typed object with slots and steps. Slots define which records the workflow acts on (the "inputs"); steps define the sequence of form screens the user fills in.

// @noverify
import type { FormDefinition } from "@stndrds/schema";

const onboardingWorkflow: FormDefinition = {
  name: "contact-onboarding",
  label: "Contact Onboarding",
  status: "published",
  version: 1,
  slots: [
    {
      id: "contact",
      objectName: "contact",
      label: "Contact",
      mode: "create",
    },
  ],
  steps: [
    {
      id: "basic-info",
      label: "Basic info",
      rows: [],
    },
    {
      id: "assign",
      label: "Assign owner",
      rows: [],
    },
  ],
};

There is no workflow() builder — workflows are plain FormDefinition objects registered via the form registry. Each FormSlot has a mode: "create", "select", "optional", or "create_if_not_empty".

Trigger

You can start a workflow in three ways:

  • UI button — call useCreateFormSubmission with the form ID and optional slot values to pre-fill records.
  • Programmatic submission — call the same hook server-side or from an automation.
  • Schedule — attach a schedule to the form definition and the runtime fires it automatically.
// @noverify
import { useCreateFormSubmission } from "@stndrds/react";

function StartOnboarding({ formId }: { formId: string }) {
  const submit = useCreateFormSubmission();
  return (
    <button
      onClick={() =>
        submit.mutate({
          formId,
          slotValues: { contact: { firstName: "Ada" } },
        })
      }
    >
      Start onboarding
    </button>
  );
}

Observe

Submissions are first-class objects. Use useFormSubmissionsByRecord to fetch all submissions linked to a specific record, or query across submissions with the dedicated hooks.

// @noverify
import { useFormSubmissionsByRecord } from "@stndrds/react";

function SubmissionHistory({ recordId }: { recordId: string }) {
  const { data } = useFormSubmissionsByRecord(recordId);
  return <ul>{data?.map((s) => <li key={s.id}>{s.status}</li>)}</ul>;
}

Submission status flows through: draftsubmittedprocessingcompleted (or failed).

See Workflows concept for the mental model behind slots, steps, and instances.

On this page