Workflows
Server-side JavaScript at .appbricx/backend/workflows/<id>.workflow.js. Trigger from webhooks, cron, CDC, or HTTP / SDK — then poll the run.
Write a workflow
/** @param {import("@appbricx/runtime").WorkflowContext} ctx */
export async function run(ctx) {
const email = ctx.trigger.payload?.email
?? ctx.trigger.payload?.body?.email;
if (!email) throw new Error("email required");
const r = await ctx.queries.run("create_lead", {
email,
source: ctx.trigger.payload?.source ?? "manual",
});
if (!r.ok) throw new Error(r.error?.message ?? "create_lead failed");
await ctx.topics.publish("leads.created", {
id: r.rows?.[0]?.id,
email,
});
return { ok: true, id: r.rows?.[0]?.id };
}Workflow id = filename without .workflow.js (e.g. on-lead-created.workflow.js → on-lead-created).
Trigger via SDK
import { runtime } from "@appbricx/runtime";
const { ok, runId, error } = await runtime.workflows.invoke("on-lead-created", {
payload: { email: "demo@example.com", source: "ui-button" },
// dryRun: true // optional — validate without side effects where supported
});
if (!ok) throw new Error(error);
const status = await runtime.workflows.getRun(runId!);
console.log(status.run);Trigger via HTTP
# Enqueue
curl -sS -X POST "$ORIGIN/__appbricx/runtime/workflows/on-lead-created/run" \
-H "Authorization: Bearer $DATA_TOKEN" \
-H "x-appbricx-data-api: 1" \
-H "content-type: application/json" \
-d '{"payload":{"email":"demo@example.com","source":"curl"}}'
# → { "ok": true, "runId": "…" }
# Inspect
curl -sS "$ORIGIN/__appbricx/runtime/runs/$RUN_ID" \
-H "Authorization: Bearer $DATA_TOKEN" \
-H "x-appbricx-data-api: 1"Other triggers
| Trigger | How | ctx.trigger.type |
|---|---|---|
| Manual / API | POST …/workflows/:id/run | manual |
| Webhook | POST /hooks/:projectId/:name | webhook |
| Cron | Schedule ticker | cron |
| CDC | Table mutation binding | cdc |
| Child | ctx.callWorkflow | call (depth ≤ 3) |
ctx surface (cheat sheet)
queries.run— preferred data accessapi.*— auto CRUD helpershttp.fetch— HTTPS only (no link-local/metadata)topics.publish/ subscribe helperssecrets.get(name)— vault / env by ref nameintegrations.invoke,messages.emailschedules.*,users.*,rbac.*log.info/warn/error— persisted on the run
How it works internally
- Invoke enqueues a row in
app_runtime_runsand loads<id>.workflow.jsfrom disk. - Runner builds a
ctxbound to the project (queries → Mustache engine → PGlite worker). - Logs stream into
app_runtime_run_logs; status is readable viaGET …/runs/:runIdor Backend → Runs in the editor. - Editor test uses the same path as HTTP invoke (
runtime.test_workflow/ Backend pane).
Related: Webhooks & schedules · Secrets