defineHook()

Creates a type-safe hook helper that ensures the payload type is consistent between hook creation and resumption.

This is a lightweight wrapper around createHook() and resumeHook() to avoid type mismatches.

We recommend using defineHook() over createHook() in production codebases for better type safety.

import { defineHook } from "workflow";

const nameHook = defineHook<{
  name: string;
}>();

export async function nameWorkflow() {
  "use workflow";

  const hook = nameHook.create();  
  const result = await hook; // Fully typed as { name: string }
  console.log('Name:', result.name);
}

API Signature

Parameters

NameTypeDescription
__0{ schema?: StandardSchemaV1<TInput, TOutput>; }Schema used to validate and transform the input payload before resuming

Returns

NameTypeDescription
create(options?: any) => Hook<T> Creates a new hook with the defined payload type.
resume(token: string, payload: T) => Promise<any> Resumes a hook by sending a payload with the defined type.

Examples

Type-Safe Hook Definition

By defining the hook once with a specific payload type, you can reuse it in multiple workflows and API routes with automatic type safety.

import { defineHook } from "workflow";

// Define once with a specific payload type
const approvalHook = defineHook<{ 
  approved: boolean; 
  comment: string; 
}>(); 

// In your workflow
export async function workflowWithApproval() {
  "use workflow";

  const hook = approvalHook.create();
  const result = await hook; // Fully typed as { approved: boolean; comment: string }

  console.log('Approved:', result.approved);
  console.log('Comment:', result.comment);
}

Resuming with Type Safety

Hooks can be resumed using the same defined hook and a token. By using the same hook, you can ensure that the payload matches the defined type when resuming a hook.

// Use the same defined hook to resume
export async function POST(request: Request) {
  const { token, approved, comment } = await request.json();

  // Type-safe resumption - TypeScript ensures the payload matches
  const result = await approvalHook.resume(token, { 
    approved, 
    comment, 
  }); 

  if (!result) {
    return Response.json({ error: 'Hook not found' }, { status: 404 });
  }

  return Response.json({ success: true, runId: result.runId });
}

Validate and Transform with Schema

The optional schema accepts any validator that conforms to Standard Schema v1External link.

Zod is shown below as one example, but libraries like Valibot, ArkType, Effect Schema, or your own custom validator work as well.

import { defineHook } from "workflow";
import { z } from "zod";

export const approvalHook = defineHook({
  // Provide a schema to validate/transform payloads.
  schema: z.object({ 
    approved: z.boolean(), 
    comment: z.string().min(1).transform((value) => value.trim()), 
  }), 
});

export async function approvalWorkflow(approvalId: string) {
  "use workflow";

  const hook = approvalHook.create({
    token: `approval:${approvalId}`,
  });

  const { approved, comment } = await hook;
  console.log('Approved:', approved);
  console.log('Comment:', comment);
}

In your route handler, resume the hook with the same definition; the schema validates and transforms the payload before the workflow continues.

export async function POST(request: Request) {
  // comment is "   Ready!   " here
  const { token, approved, comment } = await request.json();

  // If validation fails, Zod throws and the hook is not resumed.
  await approvalHook.resume(token, { 
    approved, 
    comment, // transformed to "Ready!"
  }); 

  return Response.json({ success: true });
}

Customizing Tokens

Tokens are used to identify a specific hook and for resuming a hook. You can customize the token to be more specific to a use case.

const slackHook = defineHook<{ text: string; userId: string }>();

export async function slackBotWorkflow(channelId: string) {
  "use workflow";

  const hook = slackHook.create({
    token: `slack:${channelId}`, 
  });

  const message = await hook;
  console.log(`Message from ${message.userId}: ${message.text}`);
}