resumeWebhook()
Resumes a workflow run by sending an HTTP Request to a webhook identified by its token.
This function creates a hook_received event and re-triggers the workflow to continue execution. It's designed to be called from API routes or server actions that receive external HTTP requests.
resumeWebhook is a runtime function that must be called from outside a workflow function.
import { resumeWebhook } from "workflow/api";
export async function POST(request: Request) {
  const url = new URL(request.url);
  const token = url.searchParams.get('token');
  if (!token) {
    return new Response('Missing token', { status: 400 });
  }
  const response = await resumeWebhook(token, request); 
  if (!response) {
    return new Response('Webhook not found', { status: 404 });
  }
  return response;
}API Signature
Parameters
| Name | Type | Description | 
|---|---|---|
| token | string | The unique token identifying the hook | 
| request | Request | The request to send to the hook | 
Returns
Returns a Promise<Response | null> that resolves to:
-  Response: The HTTP response from the workflow'srespondWith()call
-  null: If the webhook token is not found or invalid
Examples
Basic API Route
Forward incoming HTTP requests to a webhook by token:
import { resumeWebhook } from "workflow/api";
export async function POST(request: Request) {
  const url = new URL(request.url);
  const token = url.searchParams.get('token');
  if (!token) {
    return new Response('Token required', { status: 400 });
  }
  const response = await resumeWebhook(token, request); 
  if (!response) {
    return new Response('Webhook not found', { status: 404 });
  }
  return response; // Returns the workflow's custom response
}GitHub Webhook Handler
Handle GitHub webhook events and forward them to workflows:
import { resumeWebhook } from "workflow/api";
import { verifyGitHubSignature } from "@/lib/github";
export async function POST(request: Request) {
  // Extract repository name from URL
  const url = new URL(request.url);
  const repo = url.pathname.split('/').pop();
  // Verify GitHub signature
  const signature = request.headers.get('x-hub-signature-256');
  const isValid = await verifyGitHubSignature(request, signature);
  if (!isValid) {
    return new Response('Invalid signature', { status: 401 });
  }
  // Construct deterministic token
  const token = `github_webhook:${repo}`;
  const response = await resumeWebhook(token, request); 
  if (!response) {
    return new Response('Workflow not found', { status: 404 });
  }
  return response;
}Slack Slash Command Handler
Process Slack slash commands and route them to workflow webhooks:
import { resumeWebhook } from "workflow/api";
export async function POST(request: Request) {
  const formData = await request.formData();
  const channelId = formData.get('channel_id') as string;
  const command = formData.get('command') as string;
  // Verify Slack request signature
  const slackSignature = request.headers.get('x-slack-signature');
  if (!slackSignature) {
    return new Response('Unauthorized', { status: 401 });
  }
  // Construct token from channel ID
  const token = `slack_command:${channelId}`;
  const response = await resumeWebhook(token, request); 
  if (!response) {
    // If no workflow is listening, return a default response
    return new Response(
      JSON.stringify({
        response_type: 'ephemeral',
        text: 'No active workflow for this channel'
      }),
      {
        headers: { 'Content-Type': 'application/json' }
      }
    );
  }
  return response;
}Multi-Tenant Webhook Router
Route webhooks to different workflows based on tenant/organization:
import { resumeWebhook } from "workflow/api";
export async function POST(request: Request) {
  const url = new URL(request.url);
  // Extract tenant and webhook ID from path
  // e.g., /api/webhooks/tenant-123/webhook-abc
  const [, , , tenantId, webhookId] = url.pathname.split('/');
  if (!tenantId || !webhookId) {
    return new Response('Invalid webhook URL', { status: 400 });
  }
  // Verify API key for tenant
  const apiKey = request.headers.get('authorization');
  const isAuthorized = await verifyTenantApiKey(tenantId, apiKey);
  if (!isAuthorized) {
    return new Response('Unauthorized', { status: 401 });
  }
  // Construct namespaced token
  const token = `tenant:${tenantId}:webhook:${webhookId}`;
  const response = await resumeWebhook(token, request); 
  if (!response) {
    return new Response('Webhook not found or expired', { status: 404 });
  }
  return response;
}
async function verifyTenantApiKey(tenantId: string, apiKey: string | null) {
  // Verify API key logic
  return apiKey === process.env[`TENANT_${tenantId}_API_KEY`];
}Server Action (Next.js)
Use resumeWebhook in a Next.js server action:
'use server';
import { resumeWebhook } from "workflow/api";
export async function triggerWebhook(
  token: string,
  payload: Record<string, any>
) {
  // Create a Request object from the payload
  const request = new Request('http://localhost/webhook', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  });
  const response = await resumeWebhook(token, request);
  if (!response) {
    throw new Error('Webhook not found');
  }
  // Parse and return the response
  const contentType = response.headers.get('content-type');
  if (contentType?.includes('application/json')) {
    return await response.json();
  }
  return await response.text();
}Related Functions
-  createWebhook()- Create a webhook in a workflow
-  resumeHook()- Resume a hook with arbitrary payload
-  defineHook()- Type-safe hook helper