createHook
Create a low-level hook to resume workflows with arbitrary payloads.
Creates a hook primitive that can be used to suspend a workflow and later resume it with an arbitrary serializable payload.
Unlike createWebhook(), which always generates a random token for its public HTTP endpoint, createHook() accepts an optional custom token. If you omit token, Workflow generates a unique token automatically. Use a custom token only when the sender can deterministically reconstruct it. Hooks are resumed server-side via resumeHook() 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
}API Signature
Parameters
| Name | Type | Description |
|---|---|---|
options | HookOptions | Configuration options for the hook. |
HookOptions
| Name | Type | Description |
|---|---|---|
token | string | Unique 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. |
metadata | Serializable | Additional user-defined data to include with the hook payload. |
isWebhook | boolean | Whether 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<T>Hook
| Name | Type | Description |
|---|---|---|
token | string | The token used to identify this hook. |
dispose | () => void | Disposes 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.
The isWebhook option in HookOptions controls whether the hook can be resumed via the public webhook endpoint (/.well-known/workflow/v1/webhook/{token}). It defaults to false, meaning hooks created with createHook() can only be resumed server-side via resumeHook(). The createWebhook() function sets this to true automatically.
Examples
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);
}
}Machine-readable logging
Emit a structured log line when a hook is created so external systems can discover its token programmatically:
import { createHook } from "workflow"
export async function approvalWorkflow() {
"use workflow";
using hook = createHook<{ approved: boolean }>();
console.info(JSON.stringify({
event: "workflow.hook.created",
token: hook.token,
}));
return await hook;
}Example log line:
{"event":"workflow.hook.created","token":"nk_abc123"}Customizing Tokens
By default, Workflow generates a unique token for each hook. You can provide a custom token when the resuming side needs to reconstruct the token without prior communication.
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);
}
}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.
Related Functions
defineHook()- Type-safe hook helperresumeHook()- Resume a hook with a payloadcreateWebhook()- Higher-level HTTP webhook abstraction