Vercel Workflow
Use robotrock/workflow when you need a durable wait for human input inside Vercel Workflow — workflows that survive restarts and suspend without consuming compute until someone acts in the RobotRock inbox.
If you run agents with the Vercel AI SDK inside workflow tools or DurableAgent, see Vercel AI for mode: "workflow" on robotrock/ai tools.
Installation
npm install robotrock workflow
# or
bun add robotrock workflowworkflow is an optional peer dependency of robotrock/workflow. Install both in the app that defines your workflows.
How it works
sendToHumanInWorkflow()callscreateWebhook()and obtainswebhook.url.- A step creates the RobotRock task with that URL as the client
webhook. - The workflow suspends on
await webhookuntil RobotRock POSTs the handler payload when a human completes an action. validUntilis enforced withPromise.raceagainstsleep(timeout).
You do not configure a client webhook yourself — the SDK sets it from the workflow webhook URL.
Environment variables
Same as other SDK entry points:
ROBOTROCK_API_KEY(required)ROBOTROCK_BASE_URLorROBOTROCK_API_URL(optional)ROBOTROCK_APP(optional default inbox app bucket; override per call withappon the payload)
sendToHumanInWorkflow
Call from a function whose first statement is "use workflow":
import { sendToHumanInWorkflow } from "robotrock/workflow";
export async function deploymentApproval(version: string) {
"use workflow";
const result = await sendToHumanInWorkflow({
type: "deploy-approval",
name: `Deploy ${version}`,
description: "Approve production deployment",
actions: [
{ id: "approve", title: "Deploy now" },
{ id: "reject", title: "Block deploy" },
] as const,
context: {
data: { version },
},
});
if (result.actionId === "reject") {
throw new Error("Deploy blocked");
}
return { deployed: true, version, handledBy: result.handledBy };
}approveByHumanInWorkflow
Fixed approve / decline actions:
import { approveByHumanInWorkflow } from "robotrock/workflow";
export async function releaseGate(name: string) {
"use workflow";
const result = await approveByHumanInWorkflow({
type: "release-gate",
name,
description: "Confirm release to production",
});
return result.actionId === "approve";
}Webhook security
createWebhook() exposes a public URL at /.well-known/workflow/v1/webhook/:token. That is convenient for RobotRock callbacks but is not authenticated beyond the random token.
For stricter control, use createHook() in your own workflow and call resumeHook() from a verified API route after verifyRobotRockWebhook() — see Webhooks.
Vercel AI SDK (mode: "workflow")
When tools run inside a workflow (for example DurableAgent tool execute without "use step"), pass workflow mode so execute uses webhooks instead of polling:
import { DurableAgent } from "@workflow/ai/agent";
import { approveByHumanTool } from "robotrock/ai/workflow";
const approveByHuman = approveByHumanTool({
mode: "workflow",
app: "my-agent",
});
// Register approveByHuman on your agent tools — execute suspends until a human decidesTool approval bridge in a workflow-backed agent:
import { runWithRobotRockApprovals } from "robotrock/ai/workflow";
await runWithRobotRockApprovals({
context: { mode: "workflow", app: "my-agent" },
generate: (messages) => generateText({ model, tools, toolApproval, messages, prompt }),
});Import paths: robotrock/ai (all modes), robotrock/ai/workflow (workflow-oriented docs and re-exports).
Workflow-level vs step-level tools
| Capability | Step ("use step") | Workflow ("use workflow") |
|---|---|---|
createWebhook() | No | Yes |
RobotRock sendToHumanInWorkflow | Orchestrated from workflow | Yes |
approveByHumanTool({ mode: "workflow" }) | No — use workflow-level execute | Yes |
RobotRock API calls inside sendToHumanInWorkflow run in an internal "use step" for retries and full Node.js access. Your workflow function should stay at the workflow level and call sendToHumanInWorkflow directly.
Related docs
- Vercel AI — AI SDK tools and approval bridge modes
- Webhooks — handler payload shape
- Send to human — task and action payloads