Skip to main content
Creates a webhook that can be used to suspend and resume a workflow run upon receiving HTTP requests to a generated URL. Webhooks are specialized hooks that provide an HTTP endpoint. External systems can make HTTP requests to the webhook URL to send data into the workflow.

Signature

function createWebhook(
  options: WebhookOptions & { respondWith: 'manual' }
): Webhook<RequestWithResponse>

function createWebhook(options?: WebhookOptions): Webhook<Request>

Parameters

options
WebhookOptions
Configuration options for the webhook.

Returns

Webhook
Webhook<Request> | Webhook<RequestWithResponse>
A webhook object that extends Hook with an HTTP URL.

Usage

Basic Webhook

Create a webhook and receive HTTP requests:
export async function workflowWithWebhook() {
  "use workflow";

  const webhook = createWebhook();
  console.log('Webhook URL:', webhook.url);

  const request = await webhook;
  console.log('Received request:', request.method, request.url);
}

Custom Response

Automatically respond with a custom response:
export async function workflowWithCustomResponse() {
  "use workflow";

  const webhook = createWebhook({
    respondWith: new Response('Thank you!', {
      status: 200,
      headers: { 'Content-Type': 'text/plain' },
    }),
  });

  const request = await webhook;
  // Automatically responds with "Thank you!"
}

Manual Response

Respond manually from within a step:
import { step } from 'workflow';

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

  const webhook = createWebhook({ respondWith: 'manual' });
  console.log('Webhook URL:', webhook.url);

  const request = await webhook;

  await step(async () => {
    "use step";

    // Process the request
    const body = await request.json();
    const result = await processData(body);

    // Send response back to the caller
    await request.respondWith(
      new Response(JSON.stringify(result), {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
      })
    );
  });
}

Iterate Over Multiple Requests

Handle multiple HTTP requests:
export async function webhookListener() {
  "use workflow";

  const webhook = createWebhook();
  console.log('Listening at:', webhook.url);

  for await (const request of webhook) {
    console.log('Received:', request.method, request.url);
    
    const body = await request.text();
    if (body === 'stop') {
      break;
    }
  }
}

Parse Request Body

Extract data from the HTTP request:
export async function workflowWithRequestBody() {
  "use workflow";

  const webhook = createWebhook();

  const request = await webhook;
  
  // Parse JSON body
  const data = await request.json();
  console.log('Data:', data);

  // Or parse form data
  const formData = await request.formData();
  
  // Or get raw text
  const text = await request.text();
}

Webhook with Custom Token

Use a predictable token:
export async function githubWebhook(repoId: string) {
  "use workflow";

  const webhook = createWebhook({
    token: `github:${repoId}`,
  });

  for await (const request of webhook) {
    const event = request.headers.get('X-GitHub-Event');
    const payload = await request.json();
    console.log('GitHub event:', event, payload);
  }
}

Notes

  • Can only be called inside a workflow function (with "use workflow")
  • The webhook URL is automatically generated and includes the workflow deployment URL
  • When respondWith: 'manual' is set, you must call request.respondWith() from within a step function
  • Webhooks inherit all Hook functionality, including disposal and iteration
  • The Request object follows the Web API Request standard

Build docs developers (and LLMs) love