Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions docs/content/docs/api-reference/workflow-ai/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,35 @@ The `@workflow/ai` package is currently in active development and should be cons

Helpers for integrating AI SDK for building AI-powered workflows.

## Classes
## Root Exports (`@workflow/ai`)

The root `@workflow/ai` entrypoint exports:

<Cards>
<Card title="DurableAgent" href="/docs/api-reference/workflow-ai/durable-agent">
A class for building durable AI agents that maintain state across workflow steps and handle tool execution with automatic retries.
</Card>
<Card title="WorkflowChatTransport" href="/docs/api-reference/workflow-ai/workflow-chat-transport">
A drop-in transport for the AI SDK for automatic reconnection in interrupted streams.
</Card>
</Cards>

It also re-exports the `ModelMessage` type from the AI SDK for convenience.

## Agent (`@workflow/ai/agent`)

The `@workflow/ai/agent` subpath exports the `DurableAgent` class and related types:

<Cards>
<Card title="DurableAgent" href="/docs/api-reference/workflow-ai/durable-agent">
A class for building durable AI agents that maintain state across workflow steps and handle tool execution with automatic retries.
</Card>
</Cards>

## Provider Subpaths

Model provider wrappers are available as separate subpath imports:

- `@workflow/ai/anthropic` — Anthropic provider
- `@workflow/ai/openai` — OpenAI provider
- `@workflow/ai/google` — Google provider
- `@workflow/ai/gateway` — Gateway provider
- `@workflow/ai/xai` — xAI provider
- `@workflow/ai/test` — Mock provider for testing
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ prerequisites:
- /docs/foundations/hooks
---

Retrieves a hook by its unique token, returning the associated workflow run information and any metadata that was set when the hook was created. This function is useful for inspecting hook details before deciding whether to resume a workflow.
Retrieves a hook by its unique token, returning the associated workflow run information and any metadata that was set when the hook was created. Metadata is automatically hydrated (deserialized) before being returned, so you receive the original values rather than raw serialized data. This function is useful for inspecting hook details before deciding whether to resume a workflow.

<Callout type="warn">
`getHookByToken` is a runtime function that must be called from outside a workflow function.
Expand Down
16 changes: 8 additions & 8 deletions docs/content/docs/api-reference/workflow-api/index.mdx
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
---
title: "workflow/api"
description: Runtime functions to inspect runs, start workflows, and access world data.
description: Runtime functions for starting workflows, inspecting runs, resuming hooks, and accessing world data.
type: overview
summary: Explore runtime functions for starting workflows, inspecting runs, and managing hooks.
summary: Explore runtime functions across the workflow/api and workflow/runtime entrypoints.
---

API reference for runtime functions from the `workflow/api` package.
API reference for runtime functions from the `workflow/api` and `workflow/runtime` entrypoints.

## Functions

The API package is for access and introspection of workflow data to inspect runs, start new runs, or access anything else directly accessible by the world.
Most functions in this section are imported from `workflow/api`. `getWorld()` is imported from `workflow/runtime`.

<Cards>
<Card href="/docs/api-reference/workflow-api/start" title="start()">
Start/enqueue a new workflow run.
</Card>
<Card href="/docs/api-reference/workflow-api/resume-hook" title="resumeHook()">
Resume a workflow by sending a payload to a hook.
Resume a hook created with `createHook()` by sending an arbitrary payload.
</Card>
<Card href="/docs/api-reference/workflow-api/resume-webhook" title="resumeWebhook()">
Resume a workflow by sending a `Request` to a webhook.
Resume a webhook created with `createWebhook()` by forwarding an HTTP `Request`.
</Card>
<Card href="/docs/api-reference/workflow-api/get-hook-by-token" title="getHookByToken()">
Get hook details and metadata by its token.
Retrieve hook details, metadata, and run information by token.
</Card>
<Card href="/docs/api-reference/workflow-api/get-run" title="getRun()">
Get workflow run status and metadata without waiting for completion.
</Card>
<Card href="/docs/api-reference/workflow-api/get-world" title="getWorld()">
Get direct access to workflow storage, queuing, and streaming backends.
Get direct access to workflow storage, queuing, and streaming backends from `workflow/runtime`.
</Card>
</Cards>
103 changes: 73 additions & 30 deletions docs/content/docs/api-reference/workflow-api/resume-hook.mdx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
---
title: resumeHook
description: Resume a paused workflow by sending a payload to a hook token.
description: Resume a paused workflow by sending a payload to a hook token or hook object.
type: reference
summary: Use resumeHook to send a payload to a hook token and resume a paused workflow.
summary: Use resumeHook to send a payload to a hook token or hook object and resume a paused workflow.
prerequisites:
- /docs/foundations/hooks
related:
- /docs/api-reference/workflow-api/resume-webhook
---

Resumes a workflow run by sending a payload to a hook identified by its token.
Resumes a workflow run by sending a payload to a hook identified by its token or an existing hook object.

It creates a `hook_received` event and re-triggers the workflow to continue execution.
`resumeHook()` returns the resumed hook entity, not the workflow's eventual return value. This is useful when you need the associated `runId`, `hookId`, or hook metadata immediately after resumption.

It creates a `hook_received` event and re-queues the workflow so execution can continue. Use `resumeHook()` for hooks created with [`createHook()`](/docs/api-reference/workflow/create-hook), whether the token was explicitly set or auto-generated. For hooks created with [`createWebhook()`](/docs/api-reference/workflow/create-webhook), use [`resumeWebhook()`](/docs/api-reference/workflow-api/resume-webhook) instead.

<Callout type="warn">
`resumeHook` is a runtime function that must be called from outside a workflow function.
Expand All @@ -24,12 +26,41 @@ export async function POST(request: Request) {
const { token, data } = await request.json();

try {
const result = await resumeHook(token, data); // [!code highlight]
const hook = await resumeHook(token, data); // [!code highlight]

console.info(JSON.stringify({
event: "workflow.hook.resumed",
token: hook.token,
hookId: hook.hookId,
runId: hook.runId,
}));

return Response.json({
runId: result.runId
runId: hook.runId,
});
} catch (error) {
return new Response("Hook not found", { status: 404 });
if (error instanceof Error && error.name === "HookNotFoundError") {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: Note

HookNotFoundError is exported from workflow/errors and has a static HookNotFoundError.is(error) method (the idiomatic pattern per its own JSDoc). The string-based error.name === "HookNotFoundError" check used here (and in 3 other examples) is fragile. Consider:

import { HookNotFoundError } from "workflow/errors";
if (HookNotFoundError.is(error)) { ... }

console.warn(JSON.stringify({
event: "workflow.hook.not_found",
token,
}));

return Response.json(
{ error: "Hook not found", token },
{ status: 404 }
);
}

console.error(JSON.stringify({
event: "workflow.hook.resume_failed",
token,
error: error instanceof Error ? error.message : String(error),
}));

return Response.json(
{ error: "Failed to resume hook" },
{ status: 500 }
);
}
}
```
Expand All @@ -56,6 +87,12 @@ export default Hook;`}
showSections={["returns"]}
/>

## Error Behavior

A missing hook token is only one failure mode. `resumeHook()` can also fail while dehydrating the payload, creating the `hook_received` event, or re-queueing the workflow. In HTTP handlers, map missing hooks to `404` and unexpected failures to `500` so operational failures stay visible.

`resumeHook()` resolves to the hook record that was resumed. Use `hook.runId` with [`getRun()`](/docs/api-reference/workflow-api/get-run) if you want to inspect the workflow after resumption; it does not wait for the workflow to finish.

## Examples

### Basic API Route
Expand All @@ -69,14 +106,17 @@ export async function POST(request: Request) {
const { token, data } = await request.json();

try {
const result = await resumeHook(token, data); // [!code highlight]
const hook = await resumeHook(token, data); // [!code highlight]

return Response.json({
success: true,
runId: result.runId
runId: hook.runId
});
} catch (error) {
return new Response("Hook not found", { status: 404 });
if (error instanceof Error && error.name === "HookNotFoundError") {
return Response.json({ error: "Hook not found" }, { status: 404 });
}
return Response.json({ error: "Failed to resume hook" }, { status: 500 });
}
}
```
Expand All @@ -97,14 +137,17 @@ export async function POST(request: Request) {
const { token, approved, comment } = await request.json();

try {
const result = await resumeHook<ApprovalPayload>(token, { // [!code highlight]
const hook = await resumeHook<ApprovalPayload>(token, { // [!code highlight]
approved, // [!code highlight]
comment, // [!code highlight]
}); // [!code highlight]

return Response.json({ runId: result.runId });
return Response.json({ runId: hook.runId });
} catch (error) {
return Response.json({ error: "Invalid token" }, { status: 404 });
if (error instanceof Error && error.name === "HookNotFoundError") {
return Response.json({ error: "Hook not found" }, { status: 404 });
}
return Response.json({ error: "Failed to resume hook" }, { status: 500 });
}
}
```
Expand All @@ -120,37 +163,37 @@ import { resumeHook } from "workflow/api";

export async function approveRequest(token: string, approved: boolean) {
try {
const result = await resumeHook(token, { approved });
return result.runId;
const hook = await resumeHook(token, { approved });
return hook.runId;
} catch (error) {
throw new Error("Invalid approval token");
throw new Error("Failed to resume hook");
}
}
```

### Webhook Handler
### Passing a Hook Object

Using `resumeHook` in a generic webhook handler to resume a hook:
Instead of a token string, you can pass a `Hook` object directly (e.g. one returned by [`getHookByToken()`](/docs/api-reference/workflow-api/get-hook-by-token)):

```typescript lineNumbers
import { resumeHook } from "workflow/api";
import { getHookByToken, resumeHook } from "workflow/api";

// Generic webhook handler that forwards data to a hook
export async function POST(request: Request) {
const url = new URL(request.url);
const token = url.searchParams.get("token");

if (!token) {
return Response.json({ error: "Missing token" }, { status: 400 });
}
const { token, data } = await request.json();

try {
const body = await request.json();
const result = await resumeHook(token, body);
const hook = await getHookByToken(token);

// Validate metadata before resuming
console.log("Hook metadata:", hook.metadata);

return Response.json({ success: true, runId: result.runId });
const resumedHook = await resumeHook(hook, data); // [!code highlight]
return Response.json({ success: true, runId: resumedHook.runId });
} catch (error) {
return Response.json({ error: "Hook not found" }, { status: 404 });
if (error instanceof Error && error.name === "HookNotFoundError") {
return Response.json({ error: "Hook not found" }, { status: 404 });
}
return Response.json({ error: "Failed to resume hook" }, { status: 500 });
}
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ showSections={['parameters']}

### Returns

Returns a `Promise<Response>` that resolves to:
Returns a `Promise<Response>` that resolves to one of three outcomes:

- `Response`: The HTTP response from the workflow's `respondWith()` call
- A `202 Accepted` response when the webhook was created with the default mode (no `respondWith` option)
- The exact `Response` object configured with `createWebhook({ respondWith: new Response(...) })`
- The workflow's manual `Response` when the webhook was created with `createWebhook({ respondWith: 'manual' })` and a step calls `request.respondWith(response)`

Throws an error if the webhook token is not found or invalid.

Expand Down Expand Up @@ -81,7 +83,7 @@ export async function POST(request: Request) {

try {
const response = await resumeWebhook(token, request); // [!code highlight]
return response; // Returns the workflow's custom response
return response; // May be 202 Accepted, a configured static Response, or a manual workflow response
} catch (error) {
return new Response("Webhook not found", { status: 404 });
}
Expand Down
50 changes: 48 additions & 2 deletions docs/content/docs/api-reference/workflow-next/with-workflow.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,46 @@ prerequisites:

Configures webpack/turbopack loaders to transform workflow code (`"use step"`/`"use workflow"` directives)

## API Signature

### Parameters

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

#### Options

The second parameter accepts an optional configuration object:

| Property | Type | Description |
|---|---|---|
| `workflows.lazyDiscovery` | `boolean` | Enable lazy discovery mode. Sets the `WORKFLOW_NEXT_LAZY_DISCOVERY` flag. Deferred discovery only activates on Next.js `>= 16.2.0-canary.48`; on older versions, Workflow logs a warning and falls back to eager scanning. |
| `workflows.local.port` | `number` | Override the local development server port. Sets the `PORT` environment variable when running locally (no `VERCEL_DEPLOYMENT_ID`). |
| `workflows.local.dataDir` | `string` | Currently typed but ignored by `withWorkflow()`. In local mode, when `WORKFLOW_TARGET_WORLD` is unset, the implementation hardcodes `WORKFLOW_LOCAL_DATA_DIR` to `'.next/workflow-data'`. |
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: Note

Documenting a parameter as "typed but ignored" with the hardcoded path exposed may confuse users. Consider either omitting dataDir from the options table until it's functional, or marking it as "Reserved for future use" without exposing the hardcoded internal default.


### Returns

Returns an async function `(phase: string, ctx: { defaultConfig: NextConfig }) => Promise<NextConfig>` compatible with the `next.config.ts` default export.

### Environment Behavior

When running locally (no `VERCEL_DEPLOYMENT_ID`) and `WORKFLOW_TARGET_WORLD` is not already set:
- Sets `WORKFLOW_TARGET_WORLD` to `'local'`
- Sets `WORKFLOW_LOCAL_DATA_DIR` to `'.next/workflow-data'`

When running locally (no `VERCEL_DEPLOYMENT_ID`):
- If `workflows.local.port` is provided, sets `PORT` to that value

When running on Vercel (`VERCEL_DEPLOYMENT_ID` is present) and `WORKFLOW_TARGET_WORLD` is not already set:
- Sets `WORKFLOW_TARGET_WORLD` to `'vercel'`

During the development server phase (`phase-development-server`):
- Sets `WORKFLOW_PUBLIC_MANIFEST` to `'1'` if not already set

## Usage

To enable `"use step"` and `"use workflow"` directives while developing locally or deploying to production, wrap your `nextConfig` with `withWorkflow`.
Expand All @@ -21,8 +61,14 @@ const nextConfig: NextConfig = {
// … rest of your Next.js config
};

// not required but allows configuring workflow options
const workflowConfig = {}
// optional, but this shows the actual supported shape
const workflowConfig = {
workflows: {
local: {
port: 3001,
},
},
};

export default withWorkflow(nextConfig, workflowConfig); // [!code highlight]
```
Expand Down
Loading
Loading