Skip to main content
Workflows run in the background, but sometimes they need input from the user or want to send progress updates to the conversation. Requests and notifications are the bridge between workflows and conversations.

Requests

A request pauses the workflow and asks the conversation for data. The workflow defines what it needs, the conversation handler collects it from the user and sends it back to the workflow.

Define requests in the workflow

Declare the request schemas in the workflow definition:
import { Workflow, z } from "@botpress/runtime"

export default new Workflow({
  name: "orderWorkflow",
  requests: {
    orderId: z.object({
      orderId: z.string(),
    }),
    shippingAddress: z.object({
      street: z.string(),
      city: z.string(),
      zip: z.string(),
    }),
  },
  handler: async ({ step }) => {
    const orderData = await step.request("orderId", "Please provide the order ID.")

    const address = await step.request("shippingAddress", "Where should we ship this?")

    await step("process", async () => {
      await processOrder(orderData.orderId, address)
    })
  },
})
step.request() pauses the workflow and sends a workflow_request event to the conversation. The first argument is the name of the data being requested, and the second argument is a natural language prompt for that data.

Handle requests in the conversation

When a workflow makes a request, the conversation handler receives it as type: "workflow_request":
export default new Conversation({
  channel: "webchat.channel",
  handler: async (props) => {
    if (props.type === "workflow_request") {
      if (props.request.type === "orderWorkflow:orderId") {
        await props.request.workflow.provide(
          "orderId",
          { orderId: "ORD-12345" },
          props.request.step
        )
      }

      if (props.request.type === "orderWorkflow:shippingAddress") {
        await props.request.workflow.provide(
          "shippingAddress",
          { street: "123 Main St", city: "Springfield", zip: "62701" },
          props.request.step
        )
      }
      return
    }

    await props.execute({ instructions: "You are a helpful assistant." })
  },
})
The request.type is formatted as "workflowName:requestName". Use request.workflow.provide() to send the data back, passing request.step as the third argument so the workflow knows exactly which pending step to resume. Omitting it works when only one step is pending for that request, but throws an error if multiple are pending. The workflow resumes once the data is provided.

Let the AI handle requests

You can also let execute() handle workflow requests by passing them as context:
if (props.type === "workflow_request") {
  await props.execute({
    instructions: `A workflow needs information from the user. Ask them: ${props.request.type}`,
  })
  return
}

Notifications

A notification sends data from the workflow to the conversation without pausing the workflow. Use it for progress updates, status changes, or any information the user should see while the workflow keeps running.

Define notifications in the workflow

import { Workflow, z } from "@botpress/runtime"

export default new Workflow({
  name: "importWorkflow",
  notifications: {
    progress: z.object({
      message: z.string(),
      percentage: z.number(),
    }),
  },
  handler: async ({ step }) => {
    await step.notify("progress", { message: "Starting import...", percentage: 0 })

    await step("import-data", async () => {
      await importBatch1()
    })

    await step.notify("progress", { message: "50% complete", percentage: 50 }, "progress-50")

    await step("import-remaining", async () => {
      await importBatch2()
    })

    await step.notify("progress", { message: "Import complete!", percentage: 100 }, "progress-100")
  },
})
The third argument to step.notify() is an optional step name. Use unique step names when sending the same notification type multiple times from one handler.

Handle notifications in the conversation

Notifications arrive as type: "workflow_notify":
export default new Conversation({
  channel: "webchat.channel",
  handler: async (props) => {
    if (props.type === "workflow_notify") {
      const { message, percentage } = props.notification.payload
      await props.conversation.send({
        type: "text",
        payload: { text: `${message} (${percentage}%)` },
      })
      return
    }

    await props.execute({ instructions: "You are a helpful assistant." })
  },
})

Workflow callbacks

When a workflow completes (successfully or with failure), the conversation receives a workflow_callback event:
if (props.type === "workflow_callback") {
  const { status, output } = props.completion
  if (status === "completed") {
    await props.conversation.send({
      type: "text",
      payload: { text: `Workflow finished: ${JSON.stringify(output)}` },
    })
  }
  return
}
Last modified on April 24, 2026