@actionlayer/convex
Run tasks on any website from your Convex app. ActionLayer operates a real
browser end-to-end — bookings, signups, checkouts, form fills, data pulls —
and this component streams task status into your Convex database, so your UI
updates reactively as the task progresses.
Setup
1. Install the package:
npm install @actionlayer/convex
2. Register the component in your app's convex/convex.config.ts (create
the file if it doesn't exist):
import { defineApp } from "convex/server";
import actionlayer from "@actionlayer/convex/convex.config";
const app = defineApp();
app.use(actionlayer);
export default app;
3. Set your API key in the Convex dashboard (Settings → Environment
Variables) — get one at actionlayer.io/app#settings:
ACTIONLAYER_API_KEY=ak_...
4. Run codegen so components.actionlayer shows up in your generated API:
npx convex dev
5. Instantiate the ActionLayer client in your Convex functions and call
it — see below.
Use
import { action, query } from "./_generated/server";
import { components } from "./_generated/api";
import { ActionLayer } from "@actionlayer/convex";
import { v } from "convex/values";
const actionLayer = new ActionLayer(components.actionlayer);
export const book = action({
args: { goal: v.string() },
handler: async (ctx, args) => {
return await actionLayer.startTask(ctx, { goal: args.goal });
},
});
export const status = query({
args: { ticketId: v.string() },
handler: async (ctx, args) => {
return await actionLayer.get(ctx, args);
},
});
Your frontend subscribes with useQuery(api.tasks.status, { ticketId }) and
re-renders on every state change — no polling code on your side.
Task lifecycle
state moves through pending → completed | failed | cancelled. If the
task needs something from your user (a choice, a missing detail), it parks at
blocked_on_user with the ask in infoRequest / reason. Send the answer
back and it resumes:
await actionLayer.reply(ctx, { ticketId, message: "the 7:30pm slot works" });
cancel(ctx, { ticketId }) stops an in-flight task.
How it works
startTask calls the ActionLayer API, records the ticket in a component-owned
table, and schedules a poll loop with the Convex scheduler. The loop updates
the row until the task reaches a terminal state; every update flows to your
subscribed queries. Your app's tables are untouched — the component keeps its
state to itself.
Development
npm install
npm run typecheck
npm run build
src/component/_generated is committed — running npx convex dev in an app
that mounts the component regenerates it.
Publishing follows the standard Convex component flow — see
docs.convex.dev/components/authoring.