Create, debug, or modify Vercel WDK workflows for data updates and social media posting in the web application. Use when adding new automated jobs, fixing workflow errors, or updating scheduling logic.
This skill helps you work with Vercel WDK (Workflow Development Kit) workflows in apps/web/src/workflows/.
The project uses Vercel WDK workflows with the following structure:
apps/web/src/workflows/
├── cars.ts # Car registration data workflow
├── coe.ts # COE bidding data workflow
├── deregistrations.ts # Deregistration data workflow
├── regenerate-post.ts # Blog post regeneration workflow
└── shared.ts # Shared workflow steps (social media, cache)
Workflows are defined using Vercel WDK directives:
import { fetch } from "workflow";
export async function carsWorkflow(payload: CarsWorkflowPayload) {
"use workflow";
// Enable WDK's durable fetch for AI SDK
globalThis.fetch = fetch;
// Step 1: Process data
const result = await processCarsData();
// Step 2: Revalidate cache
await revalidateCarsCache(month, year);
// Step 3: Generate blog post
const post = await generateCarsPost(data, month);
return { postId: post.postId };
}
async function processCarsData(): Promise<UpdaterResult> {
"use step";
// Processing logic - each step is durable and can retry
return await updateCars();
}
Workflows are triggered via Vercel Cron schedules configured in apps/web/vercel.json:
{
"crons": [
{ "path": "/api/workflows/cars", "schedule": "0 10 * * *" },
{ "path": "/api/workflows/coe", "schedule": "0 10 * * *" },
{ "path": "/api/workflows/deregistrations", "schedule": "0 10 * * *" }
]
}
API routes trigger workflows using WDK's start() function:
import { start } from "workflow/api";
import { carsWorkflow } from "@web/workflows/cars";
export async function POST(request: Request) {
const payload = await request.json();
const run = await start(carsWorkflow, [payload]);
return Response.json({ message: "Workflow started", runId: run.runId });
}
Common operations are extracted to shared.ts:
export async function publishToSocialMedia(title: string, link: string) {
"use step";
await socialMediaManager.publishToAll({ message: `📰 ${title}`, link });
}
export async function revalidatePostsCache() {
"use step";
revalidateTag("posts:list", "max");
}
Steps automatically retry on failure. For custom error handling:
async function processData() {
"use step";
try {
const result = await updateData();
return result;
} catch (error) {
console.error("Step failed:", error);
throw error; // Re-throw for automatic retry
}
}
apps/web/src/workflows/"use workflow" directive"use step" directiveapps/web/src/app/api/workflows/vercel.jsonWorkflows typically need:
DATABASE_URL - PostgreSQL connectionUPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN - RedisRun workflow tests:
pnpm -F /web test -- src/workflows
Test individual workflow locally:
# Start dev server
pnpm dev
# Trigger workflow via HTTP
curl -X POST http://localhost:3000/api/workflows/cars
apps/web/src/app/api/workflows/ - Workflow route handlersapps/web/src/config/platforms.ts - Social media configurationapps/web/vercel.json - Vercel Cron schedulesapps/web/CLAUDE.md - Web application documentation"use step"globalThis.fetch = fetch for AI SDK callsSearch for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer