惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Cloudflare Blog
U
Unit 42
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
腾讯CDC
罗磊的独立博客
博客园 - 聂微东
博客园_首页
雷峰网
雷峰网
云风的 BLOG
云风的 BLOG
Jina AI
Jina AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
人人都是产品经理
人人都是产品经理
Y
Y Combinator Blog
量子位
Microsoft Azure Blog
Microsoft Azure Blog
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
月光博客
月光博客
T
The Exploit Database - CXSecurity.com
Google DeepMind News
Google DeepMind News
H
Help Net Security
O
OpenAI News
Blog — PlanetScale
Blog — PlanetScale
S
Security Affairs
S
Security @ Cisco Blogs
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
AI
AI
MongoDB | Blog
MongoDB | Blog
G
Google Developers Blog
MyScale Blog
MyScale Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
Hugging Face - Blog
Hugging Face - Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Schneier on Security
Cloudbric
Cloudbric
H
Heimdal Security Blog
J
Java Code Geeks
N
News and Events Feed by Topic
Hacker News - Newest:
Hacker News - Newest: "LLM"
宝玉的分享
宝玉的分享
有赞技术团队
有赞技术团队
S
SegmentFault 最新的问题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
爱范儿
爱范儿
I
Intezer
GbyAI
GbyAI

Workflow SDK Documentation

Patterns for Defining Tools Human-in-the-Loop Building Durable AI Agents Queueing User Messages Resumable Streams Sleep, Suspense, and Scheduling Streaming Updates from Tools API Reference Workflow Globals Changelog Resilient run start Cookbook Building a World Deploying Astro Express Fastify Hono Getting Started NestJS Next.js Nitro Nuxt Python SvelteKit Vite corrupted-event-log fetch-in-workflow hook-conflict Errors node-js-module-in-workflow serialization-failed start-invalid-workflow-function step-not-registered timeout-in-workflow webhook-invalid-respond-with-value webhook-response-not-sent workflow-not-registered Errors & Retrying Hooks & Webhooks Idempotency Foundations Serialization Starting Workflows Streaming Versioning Workflows and Steps How the Directives Work Encryption Event Sourcing Framework Integrations Understanding Directives Migration Guides Migrating from AWS Step Functions Migrating from Inngest Migrating from Temporal Observability Testing Server-Based Testing createWebhook defineHook FatalError fetch getStepMetadata getWorkflowMetadata getWritable workflow RetryableError sleep @workflow/vitest DurableAgent @workflow/ai WorkflowChatTransport getHookByToken getRun getWorld workflow/api resumeHook resumeWebhook Chat Session Modeling runtime-decryption-failed Upgrading Workflows abort-signal-timeout-in-workflow Cancellation How Cancellation Works Internal Serializable AbortController and AbortSignal Eager Processing of Steps & Incremental Event Replay TanStack Start Agent Cancellation Sequential & Parallel Execution Workflow Composition Local World | Workflow SDK Postgres World | Workflow SDK Vercel World | Workflow SDK Migrating from trigger.dev Secure Credential Handling Local World | Workflow SDK Postgres World | Workflow SDK Vercel World | Workflow SDK
createHook
2026-05-31 · via Workflow SDK Documentation

Create a low-level hook to resume workflows with arbitrary payloads.

Creates a low-level hook primitive that can be used to resume a workflow run with arbitrary payloads.

Hooks allow external systems to send data to a paused workflow without the HTTP-specific constraints of webhooks. They're identified by a token and can receive any serializable payload.

import { createHook } from "workflow"

export async function hookWorkflow() {
  "use workflow";
  // `using` automatically disposes the hook when it goes out of scope
  using hook = createHook();  
  const result = await hook; // Suspends the workflow until the hook is resumed
}

Parameters

NameTypeDescription
optionsHookOptionsConfiguration options for the hook.

HookOptions

NameTypeDescription
tokenstringUnique token that is used to associate with the hook. When specifying an explicit token, the token should be constructed with information that the dispatching side can reliably reconstruct the token with the information it has available. Deterministic tokens are intended for use with createHook() and server-side resumeHook() only. For webhooks (createWebhook()), tokens are always randomly generated to prevent unauthorized access to the public webhook endpoint. If not provided, a randomly generated token will be assigned.
metadataSerializableAdditional user-defined data to include with the hook payload.
isWebhookbooleanWhether this hook can be resumed via the public webhook endpoint. When true, the hook can be triggered by sending an HTTP request to the public /.well-known/workflow/v1/webhook/{token} URL. This is automatically set when using createWebhook(). When false (the default), the hook can only be resumed server-side via resumeHook().

Returns

Hook

NameTypeDescription
tokenstringThe token used to identify this hook.
getConflict() => Promise<Run<unknown> | null>Returns a promise that resolves with the conflicting Run if another active hook already owns this hook's token, or null once the hook has been registered and is ready to receive payloads. Calling createHook() alone does not register the hook — registration only happens when the workflow suspends. Awaiting getConflict() suspends the workflow to commit the hook registration, so it can be used to claim the token (and detect token conflicts early) without waiting for payload data. When a conflict is detected, the resolved Run is the run that currently owns the token. The workflow can decide how to handle the duplicate in code: return or log conflict.runId, inspect await conflict.status, await conflict.returnValue, or cancel the owner with await conflict.cancel() and continue in the current run. Note that awaiting the hook's payload (await hook) when the token is already owned by another active hook still rejects with HookConflictError. In the rare case where the conflicting run cannot be identified (a hook_conflict event persisted by an old world that did not record the owning run's ID), getConflict() also rejects with HookConflictError rather than resolving with an incomplete value.
dispose() => voidDisposes the hook, releasing its token for reuse by other workflows. After calling dispose(), the hook will no longer receive any events. This is useful when you want to explicitly release a hook token before the workflow completes, allowing another workflow to register a hook with the same token.

The returned Hook object also implements AsyncIterable<T>, which allows you to iterate over incoming payloads using for await...of syntax.

Use hook.getConflict() to check whether the hook token is already claimed by another active hook, without waiting for hook payload data. Calling createHook() on its own does not register the hook — registration is only committed when the workflow suspends. Awaiting hook.getConflict() suspends the workflow to commit the registration, then resolves with null once hook_created is recorded, or with { runId } identifying the conflicting run if another active hook already owns the same token.

Basic Usage

When creating a hook, you can specify a payload type for automatic type safety:

import { createHook } from "workflow"

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

  using hook = createHook<{ approved: boolean; comment: string }>(); 
  console.log("Send approval to token:", hook.token);

  const result = await hook;

  if (result.approved) {
    console.log("Approved with comment:", result.comment);
  }
}

Customizing Tokens

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

import { createHook } from "workflow";

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

  // Token constructed from channel ID
  using hook = createHook<SlackMessage>({ 
    token: `slack_messages:${channelId}`, 
  }); 

  for await (const message of hook) {
    if (message.text === "/stop") {
      break;
    }
    await processMessage(message);
  }
}

Detecting Token Conflicts

Use hook.getConflict() when the workflow needs to claim a hook token before doing other work, but does not need a payload yet:

import { createHook } from "workflow";

declare function chargeOrder(orderId: string): Promise<void>; // @setup

async function processOrder(orderId: string) {
  "use workflow";

  using hook = createHook({ 
    token: `order:${orderId}`
  }); 

  const conflict = await hook.getConflict(); 
  if (conflict) { 
    // Another active workflow run already owns this token.
    return { dedupedTo: conflict.runId };
  }

  await chargeOrder(orderId);
}

Because createHook() alone does not suspend the workflow, awaiting hook.getConflict() is what actually suspends the run and commits the hook registration. It only waits for registration — to receive payload data from a future resumeHook() call, await the hook itself or iterate it with for await...of.

On a conflict, the resolved value is { runId } identifying the run that currently owns the token. To act on the owner — inspect its status, wait for its result, or cancel it — pass conflict.runId to getRun() inside a step. See Idempotency for these strategies in context.

Waiting for Multiple Payloads

You can also wait for multiple payloads by using the for await...of syntax.

import { createHook } from "workflow"

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

  using hook = createHook<{ message: string; done?: boolean }>();

  const payloads = [];
  for await (const payload of hook) { 
    payloads.push(payload);

    if (payload.done) break;
  }

  return payloads;
}

Disposing Hooks Early

You can dispose a hook early to release its token for reuse by another workflow. This is useful for handoff patterns where one workflow needs to transfer a hook token to another workflow while still running.

import { createHook } from "workflow"

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

  const hook = createHook<{ message: string; handoff?: boolean }>({
    token: `channel:${channelId}`
  });

  for await (const payload of hook) {
    console.log("Received:", payload.message);

    if (payload.handoff) {
      hook.dispose(); // Release the token for another workflow
      break;
    }
  }

  // Continue with other work while another workflow uses the token
}

After calling dispose(), the hook will no longer receive events and its token becomes available for other workflows to use.

Automatic Disposal with using

Hooks implement the TC39 Explicit Resource Management proposal, allowing automatic disposal with the using keyword:

import { createHook } from "workflow"

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

  {
    using hook = createHook<{ message: string }>({ 
      token: `channel:${channelId}`
    });

    const payload = await hook;
    console.log("Received:", payload.message);
  } // hook is automatically disposed here

  // Token is now available for other workflows to use
  console.log("Hook disposed, continuing with other work...");
}

This is equivalent to manually calling dispose() but ensures the hook is always cleaned up, even if an error occurs.