---
title: createWebhook
description: Create webhooks to suspend and resume workflows via HTTP requests.
type: reference
summary: Use createWebhook to suspend a workflow until an HTTP request is received at a generated URL.
prerequisites:
  - /docs/foundations/hooks
related:
  - /docs/api-reference/workflow/create-hook
---

# createWebhook



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.

```ts lineNumbers
import { createWebhook } from "workflow"

export async function webhookWorkflow() {
  "use workflow";
  // `using` automatically disposes the webhook when it goes out of scope
  using webhook = createWebhook();  // [!code highlight]
  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

<TSDoc
  definition={`
import { createWebhook } from "workflow";
export default createWebhook;`}
  showSections={['parameters']}
/>

### Returns

<TSDoc
  definition={`
import { createWebhook } from "workflow";
export default createWebhook;`}
  showSections={['returns']}
/>

The returned `Webhook` object has:

* `url`: The HTTP endpoint URL that external systems can call
* `token`: The unique token identifying this webhook
* Implements `AsyncIterable<T>` for handling multiple requests, where `T` is `Request` (default) or `RequestWithResponse` (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.

<Callout type="info">
  Use the simplest option that satisfies the prompt:

  * `createWebhook()` — generated callback URL, and the default `202 Accepted` response is fine
  * `createWebhook({ respondWith: 'manual' })` — generated callback URL, but you must send a custom body, status, or headers
  * `createHook()` + `resumeHook()` — the app resumes from server-side code with a deterministic business token instead of a generated callback URL
</Callout>

<details>
  <summary>
    Common wrong turns
  </summary>

  * Do not use `respondWith: 'manual'` just because the flow has a callback URL.
  * Do not use `RequestWithResponse` unless you chose manual mode.
  * Do not invent a custom callback route when `webhook.url` is the intended callback surface.
</details>

## Examples

### Basic Usage

Create a webhook that receives HTTP requests and logs the request details:

```typescript lineNumbers
import { createWebhook } from "workflow"

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

  using webhook = createWebhook(); // [!code highlight]
  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:

```typescript lineNumbers
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.

```typescript lineNumbers
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) { // [!code highlight]
    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()`](/docs/api-reference/workflow/create-hook) — Use when the app resumes from server-side code with a deterministic business token.
* [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) — Pairs with `createHook()` for deterministic server-side resume.
* [`defineHook()`](/docs/api-reference/workflow/define-hook) — Type-safe hook helper.
* [`resumeWebhook()`](/docs/api-reference/workflow-api/resume-webhook) — Low-level runtime API. Most integrations should call `webhook.url` directly instead of adding a custom callback route.


## Sitemap
[Overview of all docs pages](/sitemap.md)
