Platformatic Workflow SDK Skill
You are an expert in building applications with the Vercel Workflow SDK
(workflow / @workflow/*) against @platformatic/world,
the self-hosted World adapter for Platformatic.
This skill helps users build apps that use workflows. It does not cover
deploying the Workflow Service — assume one is reachable at the URL the user
will supply as PLT_WORLD_SERVICE_URL.
Prerequisites Check
Before any workflow setup, verify:
Node.js Version: @platformatic/world requires Node.js v22.19.0+
node --version
Workflow Service URL: The user needs a reachable Workflow Service.
- Local dev: typically
http://localhost:3042 (started via
npx @platformatic/workflow postgresql://...).
- K8s: provided by the Platformatic control plane (ICC) or operator.
If the user does not have one, point them at
@platformatic/workflow quick start
before continuing — but do not try to start the service from this skill.
Existing package.json: This skill installs into an existing Node app.
Command Router
Based on user input ($ARGUMENTS), route to the appropriate workflow:
| Input Pattern |
Action |
init, setup, add, (empty) |
Run Detect Framework + World Setup |
next, nextjs, next.js |
Run Next.js Setup |
node, express, koa, generic |
Run Generic Node.js Setup |
fastify |
Run Fastify Setup (with plugin) |
author, write, use workflow, use step, step, hook, sleep, stream |
Read references/authoring.md |
trigger, start, run |
Run Trigger a Workflow guidance |
build, standalone |
Run Standalone Build Workflow |
env, config |
Run Environment Configuration |
status, check |
Run Status Check |
troubleshoot, debug |
Read references/troubleshooting.md |
Detect Framework + World Setup
When the user asks to "add workflow" without specifying a framework:
- Detect the framework by inspecting the project:
next.config.{js,ts,mjs} or next in package.json → Next.js Setup
fastify in package.json → Fastify Setup (with plugin)
(the plugin is optional — also offer plain Node setup)
- Any other Node server (Express/Koa/Hono/plain HTTP) → Generic Node.js Setup
- Confirm the detection with the user before generating files.
In all cases, the core install is the same:
npm install workflow @platformatic/world
workflow is the Vercel SDK; @platformatic/world is the World adapter that
points the SDK at the Platformatic Workflow Service.
Then read:
- references/world.md — full client reference (env
vars,
createWorld, world.start(), framework wiring).
- references/authoring.md — how to write
workflows and steps with the Vercel SDK (
'use workflow', 'use step',
retries, hooks, sleeps, streams, determinism rules).
Continue with the framework-specific section below for the wiring details.
Next.js Setup
When the user runs 'use workflow' / 'use step' files inside a Next.js app:
- Install:
npm install workflow @platformatic/world
- Read references/world.md — section "Next.js wiring".
- Create
instrumentation.ts at the project root:// instrumentation.ts
export async function register () {
if (process.env.PLT_WORLD_SERVICE_URL) {
const { createWorld } = await import('@platformatic/world')
const world = createWorld()
await world.start?.()
}
}
This registers the app's queue handler endpoints (/.well-known/workflow/v1/{flow,step,webhook})
with the Workflow Service on boot. In K8s + ICC, world.start() is a no-op
(ICC registers handlers via the control plane).
- Set environment variables in
.env.local:WORKFLOW_TARGET_WORLD=@platformatic/world
PLT_WORLD_SERVICE_URL=http://localhost:3042
# Optional:
PLT_WORLD_APP_ID=my-app-id # defaults to package.json name
PLT_WORLD_DEPLOYMENT_VERSION=local # auto-detected in K8s
- Author workflows the standard Vercel way — the SDK transforms
'use workflow' / 'use step' files automatically inside Next.js.
- Trigger runs from a route handler or server action:
import { start } from 'workflow/api'
import { handleSignup } from '@/workflows/signup'
await start(handleSignup, [email])
- Verify with
npm run dev and watch the Next.js logs for the
[workflow] handler registration line.
Generic Node.js Setup
For Node servers without a framework-specific transform (Express, Koa, Hono,
plain http), the SDK still works but you call world.start() explicitly.
- Install:
npm install workflow @platformatic/world
- Read references/world.md — section "Manual world.start()".
- Call
world.start() once during server boot, before accepting traffic:import { createWorld } from '@platformatic/world'
const world = createWorld()
await world.start?.()
// ...then app.listen(PORT)
- Set environment variables (see references/world.md).
- The Vercel SDK's transform is required to author
'use workflow' /
'use step' files. If the host framework does not transform them, use
the standalone build (see Standalone Build Workflow) and mount the
resulting handlers on your routes — or use the Fastify plugin which does
this for you.
Fastify Setup (with plugin)
When the user wants to host Vercel Workflow SDK workflows inside a Fastify
app, recommend @platformatic/workflow-fastify. It is optional — they can
also do the work manually following the Generic Node.js Setup.
- Install:
npm install fastify workflow @platformatic/world @platformatic/workflow-fastify
- Read references/fastify.md.
- Author workflow files (e.g.
workflows/signup.ts) with
'use workflow' / 'use step'.
- Build the standalone bundles (see Standalone Build Workflow below):
npx workflow build --target standalone
This emits flow, step, webhook, and manifest.json under
.well-known/workflow/v1/.
- Register the plugin in your Fastify app:
import Fastify from 'fastify'
import { start } from 'workflow/api'
import workflowFastify from '@platformatic/workflow-fastify'
const app = Fastify()
await app.register(workflowFastify)
app.post('/api/signup', async (req) => {
const run = await start(
{ workflowId: app.workflows.handleSignup },
[req.body.email]
)
return { runId: run.runId }
})
await app.listen({ port: Number(process.env.PORT) })
- Set environment variables (same set as Next.js / generic Node).
- Run the build before each start (or wire it into your build script):
npx workflow build --target standalone && node server.js
The plugin:
- Mounts
flow, step, webhook handlers under /.well-known/workflow/v1/.
- Decorates the Fastify instance with
app.workflows (name → workflowId map).
- Calls
world.start() on boot to register the queue handler.
Trigger a Workflow
When the user asks how to start a workflow run from application code:
- Import the trigger API from the Vercel SDK, not from
@platformatic/world:import { start } from 'workflow/api'
- Two ways to identify the workflow:
- By imported reference (works inside files the SDK transforms — typically
Next.js routes/actions):
import { handleSignup } from '@/workflows/signup'
const run = await start(handleSignup, [email])
- By workflowId string (works anywhere — required outside transformed
files, e.g. plain Fastify route handlers):
const run = await start({ workflowId: app.workflows.handleSignup }, [email])
- To resume a hook (webhook or human-in-the-loop):
import { resumeHook } from 'workflow/api'
await resumeHook(token, payload)
- Tell the user the trigger call is just an HTTP POST to the Workflow
Service — no special transport is required in the calling process.
Standalone Build Workflow
The Vercel SDK's workflow build --target standalone produces self-contained
bundles for hosts that don't have a built-in SDK transform (e.g. Fastify,
plain Node, scripts).
- The build reads
'use workflow' / 'use step' files from your source tree.
- It writes to
<buildDir>/.well-known/workflow/v1/:
flow — workflow orchestration handler (POST, Web Request => Response)
step — step execution handler
webhook — webhook resume handler
manifest.json — workflow/step ids and graph
- Extensions vary by SDK version: v5 emits
.mjs; v4 emits .js (flow/step
CommonJS, webhook ESM). @platformatic/workflow-fastify handles both
transparently.
- Re-run the build whenever workflow source files change. In CI, run it as
a pre-build step before the host app is packaged.
- Don't commit
.well-known/workflow/v1/ — add it to .gitignore alongside
dist/.
npx workflow build --target standalone
Inside Next.js you do not need this — the Next plugin transforms
workflows inline. The standalone build is for non-Next hosts.
Environment Configuration
The SDK and @platformatic/world read these environment variables:
| Variable |
Required |
Default |
Description |
WORKFLOW_TARGET_WORLD |
Yes |
— |
Set to @platformatic/world to select this adapter |
PLT_WORLD_SERVICE_URL |
Yes |
— |
Workflow Service base URL (e.g. http://localhost:3042) |
PLT_WORLD_APP_ID |
No |
package.json name |
Application identifier (multi-tenant routing) |
PLT_WORLD_DEPLOYMENT_VERSION |
No |
K8s label or 'local' |
Version each run pins to |
PORT |
No |
— |
Used by world.start() to register the callback URL locally |
In K8s, PLT_WORLD_DEPLOYMENT_VERSION is auto-detected from the pod's
plt.dev/version label via the K8s API — usually leave it unset there.
For local development without K8s, the Workflow Service runs in single-tenant
mode and any appId is accepted.
Status Check
When user runs /workflow status:
Node.js Version
node --version
Check if >= v22.19.0.
Dependencies present
node -e "require('workflow/package.json')"
node -e "require('@platformatic/world/package.json')"
Required env vars set
node -e "console.log({world:process.env.WORKFLOW_TARGET_WORLD, url:process.env.PLT_WORLD_SERVICE_URL})"
WORKFLOW_TARGET_WORLD must equal @platformatic/world.
PLT_WORLD_SERVICE_URL must be a reachable URL.
Service reachable
curl -fsS "$PLT_WORLD_SERVICE_URL/api/v1/apps/default/runs" >/dev/null && echo OK
For Next.js: instrumentation.ts exists at the project root and
calls world.start() inside register().
For Fastify with plugin: .well-known/workflow/v1/manifest.json exists
(i.e. the standalone build has been run).
Report findings in a clear format:
Workflow SDK Configuration Status
=================================
Node.js Version: vX.X.X [OK/UPGRADE NEEDED]
workflow installed: [vX.X.X/Missing]
@platformatic/world: [vX.X.X/Missing]
WORKFLOW_TARGET_WORLD: [@platformatic/world/Missing]
PLT_WORLD_SERVICE_URL: [URL/Missing]
Service reachable: [OK/Unreachable]
Framework wiring: [instrumentation.ts/Fastify plugin/manual]
Standalone build: [Present/Not built/N/A]
Important Notes
@platformatic/world is a runtime drop-in for @workflow/world. The
Vercel SDK calls into it via the standard World interface; nothing
application-level changes.
world.start() must run exactly once per process before accepting
traffic. Calling it twice is harmless (idempotent) but wastes a call.
Skipping it means queue messages will never reach this process and runs
will hang in pending.
- Under K8s + ICC,
world.start() is a no-op — ICC registers handlers
centrally via the control plane.
- Spec version:
@platformatic/world declares spec v3 (CBOR queue
transport). It interoperates with v2 servers and v2 clients, so version
skew during rollout is safe.
- SDK compatibility: works at runtime against both
workflow@4.2.x
(stable) and workflow@5.0.0-beta.x (next). The world is typed against
@workflow/world@4.1.1 but exposes the v5 streams.* namespace at
runtime alongside the v4 flat methods.
Troubleshooting
For common issues (runs stuck in pending, world.start() failing, missing
callback URL, K8s deployment version mismatch), read
references/troubleshooting.md.
1---2name: workflow3description: Build applications that use the Vercel Workflow SDK against a self-hosted Platformatic Workflow Service via `@platformatic/world`. Use when users ask to: - "add workflow to my app", "use workflow sdk", "self-host vercel workflow" - "set up @platformatic/world", "platformatic world", "PLT_WORLD_SERVICE_URL" - "trigger a workflow run", "start a workflow", "resume a hook" - "wire workflow in Next.js", "instrumentation.ts for workflow" - "use workflow with Fastify", "@platformatic/workflow-fastify" - "workflow build --target standalone", "callback handlers", ".well-known/workflow" - "queue handler", "world.start()", "register handlers" Covers `@platformatic/world` (the World adapter), the Vercel Workflow SDK `'use workflow'` / `'use step'` authoring model, framework wiring (Next.js via `instrumentation.ts`, Node/Express/etc. via explicit `world.start()`), and the optional `@platformatic/workflow-fastify` plugin for mounting standalone-build handlers on Fastify. Does NOT cover deploying or operating the Workflow S4---56# Platformatic Workflow SDK Skill78You are an expert in building applications with the Vercel Workflow SDK9(`workflow` / `@workflow/*`) against [`@platformatic/world`](https://github.com/platformatic/platformatic-world),10the self-hosted World adapter for Platformatic.1112This skill helps users build apps that **use** workflows. It does not cover13deploying the Workflow Service — assume one is reachable at the URL the user14will supply as `PLT_WORLD_SERVICE_URL`.1516## Prerequisites Check1718Before any workflow setup, verify:19201. **Node.js Version**: `@platformatic/world` requires Node.js v22.19.0+21 ```bash22 node --version23 ```24252. **Workflow Service URL**: The user needs a reachable Workflow Service.26 - Local dev: typically `http://localhost:3042` (started via27 `npx @platformatic/workflow postgresql://...`).28 - K8s: provided by the Platformatic control plane (ICC) or operator.29 If the user does not have one, point them at30 [`@platformatic/workflow` quick start](https://github.com/platformatic/platformatic-world#quick-start)31 before continuing — but do not try to start the service from this skill.32333. **Existing package.json**: This skill installs into an existing Node app.3435## Command Router3637Based on user input ($ARGUMENTS), route to the appropriate workflow:3839| Input Pattern | Action |40|---|---|41| `init`, `setup`, `add`, (empty) | Run **Detect Framework + World Setup** |42| `next`, `nextjs`, `next.js` | Run **Next.js Setup** |43| `node`, `express`, `koa`, `generic` | Run **Generic Node.js Setup** |44| `fastify` | Run **Fastify Setup (with plugin)** |45| `author`, `write`, `use workflow`, `use step`, `step`, `hook`, `sleep`, `stream` | Read [references/authoring.md](references/authoring.md) |46| `trigger`, `start`, `run` | Run **Trigger a Workflow** guidance |47| `build`, `standalone` | Run **Standalone Build Workflow** |48| `env`, `config` | Run **Environment Configuration** |49| `status`, `check` | Run **Status Check** |50| `troubleshoot`, `debug` | Read [references/troubleshooting.md](references/troubleshooting.md) |5152---5354## Detect Framework + World Setup5556When the user asks to "add workflow" without specifying a framework:57581. Detect the framework by inspecting the project:59 - `next.config.{js,ts,mjs}` or `next` in `package.json` → **Next.js Setup**60 - `fastify` in `package.json` → **Fastify Setup (with plugin)**61 (the plugin is optional — also offer plain Node setup)62 - Any other Node server (Express/Koa/Hono/plain HTTP) → **Generic Node.js Setup**632. Confirm the detection with the user before generating files.6465In all cases, the core install is the same:6667```bash68npm install workflow @platformatic/world69```7071`workflow` is the Vercel SDK; `@platformatic/world` is the World adapter that72points the SDK at the Platformatic Workflow Service.7374Then read:7576- [references/world.md](references/world.md) — full client reference (env77 vars, `createWorld`, `world.start()`, framework wiring).78- [references/authoring.md](references/authoring.md) — how to write79 workflows and steps with the Vercel SDK (`'use workflow'`, `'use step'`,80 retries, hooks, sleeps, streams, determinism rules).8182Continue with the framework-specific section below for the wiring details.8384---8586## Next.js Setup8788When the user runs `'use workflow'` / `'use step'` files inside a Next.js app:89901. Install:91 ```bash92 npm install workflow @platformatic/world93 ```942. Read [references/world.md](references/world.md) — section "Next.js wiring".953. Create `instrumentation.ts` at the project root:96 ```ts97 // instrumentation.ts98 export async function register () {99 if (process.env.PLT_WORLD_SERVICE_URL) {100 const { createWorld } = await import('@platformatic/world')101 const world = createWorld()102 await world.start?.()103 }104 }105 ```106 This registers the app's queue handler endpoints (`/.well-known/workflow/v1/{flow,step,webhook}`)107 with the Workflow Service on boot. In K8s + ICC, `world.start()` is a no-op108 (ICC registers handlers via the control plane).1094. Set environment variables in `.env.local`:110 ```111 WORKFLOW_TARGET_WORLD=@platformatic/world112 PLT_WORLD_SERVICE_URL=http://localhost:3042113 # Optional:114 PLT_WORLD_APP_ID=my-app-id # defaults to package.json name115 PLT_WORLD_DEPLOYMENT_VERSION=local # auto-detected in K8s116 ```1175. Author workflows the standard Vercel way — the SDK transforms118 `'use workflow'` / `'use step'` files automatically inside Next.js.1196. Trigger runs from a route handler or server action:120 ```ts121 import { start } from 'workflow/api'122 import { handleSignup } from '@/workflows/signup'123124 await start(handleSignup, [email])125 ```1267. Verify with `npm run dev` and watch the Next.js logs for the127 `[workflow]` handler registration line.128129---130131## Generic Node.js Setup132133For Node servers without a framework-specific transform (Express, Koa, Hono,134plain `http`), the SDK still works but you call `world.start()` explicitly.1351361. Install:137 ```bash138 npm install workflow @platformatic/world139 ```1402. Read [references/world.md](references/world.md) — section "Manual world.start()".1413. Call `world.start()` once during server boot, before accepting traffic:142 ```ts143 import { createWorld } from '@platformatic/world'144145 const world = createWorld()146 await world.start?.()147 // ...then app.listen(PORT)148 ```1494. Set environment variables (see [references/world.md](references/world.md)).1505. The Vercel SDK's transform is required to author `'use workflow'` /151 `'use step'` files. If the host framework does not transform them, use152 the standalone build (see **Standalone Build Workflow**) and mount the153 resulting handlers on your routes — or use the Fastify plugin which does154 this for you.155156---157158## Fastify Setup (with plugin)159160When the user wants to host Vercel Workflow SDK workflows inside a Fastify161app, recommend `@platformatic/workflow-fastify`. It is **optional** — they can162also do the work manually following the Generic Node.js Setup.1631641. Install:165 ```bash166 npm install fastify workflow @platformatic/world @platformatic/workflow-fastify167 ```1682. Read [references/fastify.md](references/fastify.md).1693. Author workflow files (e.g. `workflows/signup.ts`) with170 `'use workflow'` / `'use step'`.1714. Build the standalone bundles (see **Standalone Build Workflow** below):172 ```bash173 npx workflow build --target standalone174 ```175 This emits `flow`, `step`, `webhook`, and `manifest.json` under176 `.well-known/workflow/v1/`.1775. Register the plugin in your Fastify app:178 ```ts179 import Fastify from 'fastify'180 import { start } from 'workflow/api'181 import workflowFastify from '@platformatic/workflow-fastify'182183 const app = Fastify()184 await app.register(workflowFastify)185186 app.post('/api/signup', async (req) => {187 const run = await start(188 { workflowId: app.workflows.handleSignup },189 [req.body.email]190 )191 return { runId: run.runId }192 })193194 await app.listen({ port: Number(process.env.PORT) })195 ```1966. Set environment variables (same set as Next.js / generic Node).1977. Run the build before each start (or wire it into your build script):198 ```bash199 npx workflow build --target standalone && node server.js200 ```201202The plugin:203- Mounts `flow`, `step`, `webhook` handlers under `/.well-known/workflow/v1/`.204- Decorates the Fastify instance with `app.workflows` (name → workflowId map).205- Calls `world.start()` on boot to register the queue handler.206207---208209## Trigger a Workflow210211When the user asks how to start a workflow run from application code:2122131. Import the trigger API from the Vercel SDK, not from `@platformatic/world`:214 ```ts215 import { start } from 'workflow/api'216 ```2172. Two ways to identify the workflow:218 - **By imported reference** (works inside files the SDK transforms — typically219 Next.js routes/actions):220 ```ts221 import { handleSignup } from '@/workflows/signup'222 const run = await start(handleSignup, [email])223 ```224 - **By workflowId string** (works anywhere — required outside transformed225 files, e.g. plain Fastify route handlers):226 ```ts227 const run = await start({ workflowId: app.workflows.handleSignup }, [email])228 ```2293. To resume a hook (webhook or human-in-the-loop):230 ```ts231 import { resumeHook } from 'workflow/api'232 await resumeHook(token, payload)233 ```2344. Tell the user the trigger call is just an HTTP POST to the Workflow235 Service — no special transport is required in the calling process.236237---238239## Standalone Build Workflow240241The Vercel SDK's `workflow build --target standalone` produces self-contained242bundles for hosts that don't have a built-in SDK transform (e.g. Fastify,243plain Node, scripts).2442451. The build reads `'use workflow'` / `'use step'` files from your source tree.2462. It writes to `<buildDir>/.well-known/workflow/v1/`:247 - `flow` — workflow orchestration handler (POST, Web `Request => Response`)248 - `step` — step execution handler249 - `webhook` — webhook resume handler250 - `manifest.json` — workflow/step ids and graph2513. Extensions vary by SDK version: v5 emits `.mjs`; v4 emits `.js` (flow/step252 CommonJS, webhook ESM). `@platformatic/workflow-fastify` handles both253 transparently.2544. Re-run the build whenever workflow source files change. In CI, run it as255 a pre-build step before the host app is packaged.2565. Don't commit `.well-known/workflow/v1/` — add it to `.gitignore` alongside257 `dist/`.258259```bash260npx workflow build --target standalone261```262263Inside Next.js you do **not** need this — the Next plugin transforms264workflows inline. The standalone build is for non-Next hosts.265266---267268## Environment Configuration269270The SDK and `@platformatic/world` read these environment variables:271272| Variable | Required | Default | Description |273|---|---|---|---|274| `WORKFLOW_TARGET_WORLD` | Yes | — | Set to `@platformatic/world` to select this adapter |275| `PLT_WORLD_SERVICE_URL` | Yes | — | Workflow Service base URL (e.g. `http://localhost:3042`) |276| `PLT_WORLD_APP_ID` | No | `package.json` `name` | Application identifier (multi-tenant routing) |277| `PLT_WORLD_DEPLOYMENT_VERSION` | No | K8s label or `'local'` | Version each run pins to |278| `PORT` | No | — | Used by `world.start()` to register the callback URL locally |279280In K8s, `PLT_WORLD_DEPLOYMENT_VERSION` is auto-detected from the pod's281`plt.dev/version` label via the K8s API — usually leave it unset there.282283For local development without K8s, the Workflow Service runs in single-tenant284mode and any `appId` is accepted.285286---287288## Status Check289290When user runs `/workflow status`:2912921. **Node.js Version**293 ```bash294 node --version295 ```296 Check if >= v22.19.0.2972982. **Dependencies present**299 ```bash300 node -e "require('workflow/package.json')"301 node -e "require('@platformatic/world/package.json')"302 ```3033043. **Required env vars set**305 ```bash306 node -e "console.log({world:process.env.WORKFLOW_TARGET_WORLD, url:process.env.PLT_WORLD_SERVICE_URL})"307 ```308 `WORKFLOW_TARGET_WORLD` must equal `@platformatic/world`.309 `PLT_WORLD_SERVICE_URL` must be a reachable URL.3103114. **Service reachable**312 ```bash313 curl -fsS "$PLT_WORLD_SERVICE_URL/api/v1/apps/default/runs" >/dev/null && echo OK314 ```3153165. **For Next.js**: `instrumentation.ts` exists at the project root and317 calls `world.start()` inside `register()`.3183196. **For Fastify with plugin**: `.well-known/workflow/v1/manifest.json` exists320 (i.e. the standalone build has been run).321322Report findings in a clear format:323324```325Workflow SDK Configuration Status326=================================327Node.js Version: vX.X.X [OK/UPGRADE NEEDED]328workflow installed: [vX.X.X/Missing]329@platformatic/world: [vX.X.X/Missing]330WORKFLOW_TARGET_WORLD: [@platformatic/world/Missing]331PLT_WORLD_SERVICE_URL: [URL/Missing]332Service reachable: [OK/Unreachable]333Framework wiring: [instrumentation.ts/Fastify plugin/manual]334Standalone build: [Present/Not built/N/A]335```336337---338339## Important Notes340341- **`@platformatic/world` is a runtime drop-in for `@workflow/world`.** The342 Vercel SDK calls into it via the standard World interface; nothing343 application-level changes.344- **`world.start()` must run exactly once per process before accepting345 traffic.** Calling it twice is harmless (idempotent) but wastes a call.346 Skipping it means queue messages will never reach this process and runs347 will hang in `pending`.348- **Under K8s + ICC**, `world.start()` is a no-op — ICC registers handlers349 centrally via the control plane.350- **Spec version**: `@platformatic/world` declares spec v3 (CBOR queue351 transport). It interoperates with v2 servers and v2 clients, so version352 skew during rollout is safe.353- **SDK compatibility**: works at runtime against both `workflow@4.2.x`354 (stable) and `workflow@5.0.0-beta.x` (next). The world is typed against355 `@workflow/world@4.1.1` but exposes the v5 `streams.*` namespace at356 runtime alongside the v4 flat methods.357358## Troubleshooting359360For common issues (runs stuck in `pending`, `world.start()` failing, missing361callback URL, K8s deployment version mismatch), read362[references/troubleshooting.md](references/troubleshooting.md).