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

推荐订阅源

小众软件
小众软件
IT之家
IT之家
博客园 - 聂微东
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Privacy International News Feed
人人都是产品经理
人人都是产品经理
PCI Perspectives
PCI Perspectives
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
V
Vulnerabilities – Threatpost
美团技术团队
S
Secure Thoughts
N
News | PayPal Newsroom
L
LINUX DO - 最新话题
腾讯CDC
Application and Cybersecurity Blog
Application and Cybersecurity Blog
雷峰网
雷峰网
B
Blog
MyScale Blog
MyScale Blog
T
The Blog of Author Tim Ferriss
TaoSecurity Blog
TaoSecurity Blog
N
News and Events Feed by Topic
Blog — PlanetScale
Blog — PlanetScale
C
Check Point Blog
T
Tailwind CSS Blog
月光博客
月光博客
Simon Willison's Weblog
Simon Willison's Weblog
Hacker News: Ask HN
Hacker News: Ask HN
The Last Watchdog
The Last Watchdog
Google DeepMind News
Google DeepMind News
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
MongoDB | Blog
MongoDB | Blog
S
Security @ Cisco Blogs
Jina AI
Jina AI
Engineering at Meta
Engineering at Meta
S
Security Affairs
Forbes - Security
Forbes - Security
P
Palo Alto Networks Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
T
Tor Project blog
O
OpenAI News
L
Lohrmann on Cybersecurity
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Proofpoint News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
LangChain Blog
B
Blog RSS Feed
H
Hackread – Cybersecurity News, Data Breaches, AI and More

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.