createWebhook
Create webhooks to suspend and resume workflows via HTTP requests.
Creates a webhook that can be used to suspend and resume a workflow run upon receiving an HTTP request.
Webhooks provide a way for external systems to send HTTP requests directly to your workflow. Unlike hooks which accept arbitrary payloads, webhooks work with standard HTTP Request objects and can return HTTP Response objects.
import { createWebhook } from "workflow"
export async function webhookWorkflow() {
"use workflow";
// `using` automatically disposes the webhook when it goes out of scope
using webhook = createWebhook();
console.log("Webhook URL:", webhook.url);
const request = await webhook; // Suspends until HTTP request received
console.log("Received request:", request.method, request.url);
}API Signature
Parameters
Signature 1
| Name | Type | Description |
|---|---|---|
options | WebhookOptions & { respondWith: "manual"; } |
Signature 2
| Name | Type | Description |
|---|---|---|
options | WebhookOptions |
Returns
Signature 1
Webhook<RequestWithResponse>Signature 2
Webhook<Request>The returned Webhook object has:
url: The HTTP endpoint URL that external systems can calltoken: The unique token identifying this webhook- Implements
AsyncIterable<T>for handling multiple requests, whereTisRequest(default) orRequestWithResponse(manual mode)
When using createWebhook({ respondWith: 'manual' }), the resolved request type is RequestWithResponse, which extends the standard Request interface with a respondWith(response: Response): Promise<void> method for sending custom responses back to the caller.
Use the simplest option that satisfies the prompt:
createWebhook()— generated callback URL, and the default202 Acceptedresponse is finecreateWebhook({ respondWith: 'manual' })— generated callback URL, but you must send a custom body, status, or headerscreateHook()+resumeHook()— the app resumes from server-side code with a deterministic business token instead of a generated callback URL
Common wrong turns
- Do not use
respondWith: 'manual'just because the flow has a callback URL. - Do not use
RequestWithResponseunless you chose manual mode. - Do not invent a custom callback route when
webhook.urlis the intended callback surface.
Examples
Basic Usage
Create a webhook that receives HTTP requests and logs the request details:
import { createWebhook } from "workflow"
export async function basicWebhookWorkflow() {
"use workflow";
using webhook = createWebhook();
console.log("Send requests to:", webhook.url);
const request = await webhook;
console.log("Method:", request.method);
console.log("Headers:", Object.fromEntries(request.headers));
const body = await request.text();
console.log("Body:", body);
}Responding to Webhook Requests (Manual Mode)
Use this section only when the caller requires a non-default HTTP response. If 202 Accepted is acceptable, use createWebhook() without respondWith: "manual".
Pass { respondWith: "manual" } to get a RequestWithResponse object with a respondWith() method. Note that respondWith() must be called from within a step function:
import { createWebhook, type RequestWithResponse } from "workflow"
async function sendResponse(request: RequestWithResponse): Promise<void> {
"use step";
await request.respondWith(
new Response(JSON.stringify({ success: true, message: "Received!" }), {
status: 200,
headers: { "Content-Type": "application/json" }
})
);
}
export async function respondingWebhookWorkflow() {
"use workflow";
using webhook = createWebhook({ respondWith: "manual" });
console.log("Webhook URL:", webhook.url);
const request = await webhook;
// Send a custom response back to the caller
await sendResponse(request);
// Continue workflow processing
const data = await request.json();
await processData(data);
}
async function processData(data: any): Promise<void> {
"use step";
// Process the webhook data
console.log("Processing:", data);
}Waiting for Multiple Requests
You can also wait for multiple requests by using the for await...of syntax.
import { createWebhook, type RequestWithResponse } from "workflow"
async function sendAck(request: RequestWithResponse, message: string) {
"use step";
await request.respondWith(
Response.json({ received: true, message })
);
}
async function processEvent(data: any) {
"use step";
console.log("Processing event:", data);
}
export async function eventCollectorWorkflow() {
"use workflow";
using webhook = createWebhook({ respondWith: "manual" });
console.log("Send events to:", webhook.url);
for await (const request of webhook) {
const data = await request.json();
if (data.type === "done") {
await sendAck(request, "Workflow complete");
break;
}
await sendAck(request, "Event received");
await processEvent(data);
}
}Related Functions
createHook()— Use when the app resumes from server-side code with a deterministic business token.resumeHook()— Pairs withcreateHook()for deterministic server-side resume.defineHook()— Type-safe hook helper.resumeWebhook()— Low-level runtime API. Most integrations should callwebhook.urldirectly instead of adding a custom callback route.